2012-06-20 9 views
9

Voglio generare una tabella HTML da un paio di parametri specificati. In particolare, i due parametri che voglio passare nel mio metodo sono: lista IEnumerable, e alcuni sottoinsieme di proprietà di T. Per esempio, diciamo che ho una lista di questa classe:Genera tabella HTML da un elenco di classi generiche con proprietà specificate

class Person 
{ 
    string FirstName 
    string MiddleName 
    string LastName 
} 

Diciamo che la lista ha 5 persone in esso. Voglio essere in grado di ottenere una tabella HTML di quella classe (o qualsiasi altra classe arbitraria) facendo qualcosa di simile a questo:

List<Person> people; 
...add people to list 

string HTML = GetMyTable(people, "FirstName", "LastName"); 

Sono sicuro che c'è un modo migliore per specificare quali proprietà voglio la tabella generata da (o quali proprietà voglio escludere dal tavolo, sarebbe meglio visto che di solito voglio la maggior parte o tutte le proprietà della classe), ma non sono sicuro di come (non ho mai usato il reflection, ma sono indovinando è così). Inoltre, il metodo dovrebbe accettare un elenco di qualsiasi tipo di classe.

Qualche idea intelligente su come realizzare questo?

risposta

19

Forse qualcosa del genere?

var html = GetMyTable(people, x => x.LastName, x => x.FirstName); 

public static string GetMyTable<T>(IEnumerable<T> list,params Func<T,object>[] fxns) 
{ 

    StringBuilder sb = new StringBuilder(); 
    sb.Append("<TABLE>\n"); 
    foreach (var item in list) 
    { 
     sb.Append("<TR>\n"); 
     foreach(var fxn in fxns) 
     { 
      sb.Append("<TD>"); 
      sb.Append(fxn(item)); 
      sb.Append("</TD>"); 
     } 
     sb.Append("</TR>\n"); 
    } 
    sb.Append("</TABLE>"); 

    return sb.ToString(); 
} 

--version 2.0--

public static string GetMyTable<T>(IEnumerable<T> list, params Expression<Func<T, object>>[] fxns) 
{ 

    StringBuilder sb = new StringBuilder(); 
    sb.Append("<TABLE>\n"); 

    sb.Append("<TR>\n"); 
    foreach (var fxn in fxns) 
    { 
     sb.Append("<TD>"); 
     sb.Append(GetName(fxn)); 
     sb.Append("</TD>"); 
    } 
    sb.Append("</TR> <!-- HEADER -->\n"); 


    foreach (var item in list) 
    { 
     sb.Append("<TR>\n"); 
     foreach (var fxn in fxns) 
     { 
      sb.Append("<TD>"); 
      sb.Append(fxn.Compile()(item)); 
      sb.Append("</TD>"); 
     } 
     sb.Append("</TR>\n"); 
    } 
    sb.Append("</TABLE>"); 

    return sb.ToString(); 
} 

static string GetName<T>(Expression<Func<T, object>> expr) 
{ 
    var member = expr.Body as MemberExpression; 
    if (member != null) 
     return GetName2(member); 

    var unary = expr.Body as UnaryExpression; 
    if (unary != null) 
     return GetName2((MemberExpression)unary.Operand); 

    return "?+?"; 
} 

static string GetName2(MemberExpression member) 
{ 
    var fieldInfo = member.Member as FieldInfo; 
    if (fieldInfo != null) 
    { 
     var d = fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute)) as DescriptionAttribute; 
     if (d != null) return d.Description; 
     return fieldInfo.Name; 
    } 

    var propertInfo = member.Member as PropertyInfo; 
    if (propertInfo != null) 
    { 
     var d = propertInfo.GetCustomAttribute(typeof(DescriptionAttribute)) as DescriptionAttribute; 
     if (d != null) return d.Description; 
     return propertInfo.Name; 
    } 

    return "?-?"; 
} 

PS: Calling fxn.Compile() volte può essere killer di prestazioni in un loop stretto. Può essere meglio memorizzarlo in un dizionario.

+0

Cosa succede se si tenta di selezionare un int e una stringa, ad esempio? 'P' diventerà' object', o non verrà compilato? –

+0

@TimS. Grazie ho aggiornato la risposta. –

+0

Puoi spiegare cosa sta facendo "fxn (item)"? – birdus

3

Qui ci sono due approcci, uno che utilizza la riflessione:

public static string GetMyTable(IEnumerable list, params string[] columns) 
{ 
    var sb = new StringBuilder(); 
    foreach (var item in list) 
    { 
     //todo this should actually make an HTML table, not just get the properties requested 
     foreach (var column in columns) 
      sb.Append(item.GetType().GetProperty(column).GetValue(item, null)); 
    } 
    return sb.ToString(); 
} 
//used like 
string HTML = GetMyTable(people, "FirstName", "LastName"); 

o utilizzando lambda:

public static string GetMyTable<T>(IEnumerable<T> list, params Func<T, object>[] columns) 
{ 
    var sb = new StringBuilder(); 
    foreach (var item in list) 
    { 
     //todo this should actually make an HTML table, not just get the properties requested 
     foreach (var column in columns) 
      sb.Append(column(item)); 
    } 
    return sb.ToString(); 
} 
//used like 
string HTML = GetMyTable(people, x => x.FirstName, x => x.LastName); 

Con le lambda, quello che sta succedendo è che stai passando metodi al metodo GetMyTable per ottenere ogni proprietà. Ciò ha benefici sul riflesso come una forte digitazione e probabilmente prestazioni.

+0

Grazie per l'aiuto, Tim. Apprezzo vedere diversi modi per farlo. – birdus

6

Questo è quello che ho fatto e sembra funzionare bene e non un enorme successo di prestazioni.

public static string ToHtmlTable<T>(this List<T> listOfClassObjects) 
    { 
     var ret = string.Empty; 

     return listOfClassObjects == null || !listOfClassObjects.Any() 
      ? ret 
      : "<table>" + 
       listOfClassObjects.First().GetType().GetProperties().Select(p => p.Name).ToList().ToColumnHeaders() + 
       listOfClassObjects.Aggregate(ret, (current, t) => current + t.ToHtmlTableRow()) + 
       "</table>"; 
    } 

    public static string ToColumnHeaders<T>(this List<T> listOfProperties) 
    { 
     var ret = string.Empty; 

     return listOfProperties == null || !listOfProperties.Any() 
      ? ret 
      : "<tr>" + 
       listOfProperties.Aggregate(ret, 
        (current, propValue) => 
         current + 
         ("<th style='font-size: 11pt; font-weight: bold; border: 1pt solid black'>" + 
         (Convert.ToString(propValue).Length <= 100 
          ? Convert.ToString(propValue) 
          : Convert.ToString(propValue).Substring(0, 100)) + "..." + "</th>")) + 
       "</tr>"; 
    } 

    public static string ToHtmlTableRow<T>(this T classObject) 
    { 
     var ret = string.Empty; 

     return classObject == null 
      ? ret 
      : "<tr>" + 
       classObject.GetType() 
        .GetProperties() 
        .Aggregate(ret, 
         (current, prop) => 
          current + ("<td style='font-size: 11pt; font-weight: normal; border: 1pt solid black'>" + 
            (Convert.ToString(prop.GetValue(classObject, null)).Length <= 100 
             ? Convert.ToString(prop.GetValue(classObject, null)) 
             : Convert.ToString(prop.GetValue(classObject, null)).Substring(0, 100) + 
              "...") + 
            "</td>")) + "</tr>"; 
    } 

Per utilizzarlo basta passare il ToHtmlTable() un elenco Esempio:

documenti list = GetMyListOfDocuments(); var table = documents.ToHtmlTable();

Problemi correlati