C# 从具有指定属性的泛型类列表生成 HTML 表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11126137/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generate HTML table from list of generic class with specified properties
提问by birdus
I want to generate an HTML table from a couple specified parameters. Specifically, the two parameters I want to pass into my method are: IEnumerable list, and some subset of properties of T. For example, let's say I have a List of this class:
我想从几个指定的参数生成一个 HTML 表。具体来说,我想传递给我的方法的两个参数是:IEnumerable 列表,以及 T 的一些属性子集。例如,假设我有一个此类的 List:
class Person
{
string FirstName
string MiddleName
string LastName
}
Let's say the list has 5 people in it. I want to be able to get an HTML table of that class (or any other arbitrary class) by doing something like this:
假设列表中有 5 个人。我希望能够通过执行以下操作来获取该类(或任何其他任意类)的 HTML 表:
List<Person> people;
...add people to list
string HTML = GetMyTable(people, "FirstName", "LastName");
I'm sure there's a better way to specify which properties I want the table generated from (or which properties I want excluded from the table, that would be better since I'll usually want most or all of the class's properties), but I'm not sure how (I've never used reflection, but I'm guessing that's how). Also, the method should accept a list of any type of class.
我确信有一种更好的方法来指定我想要生成的表的哪些属性(或者我想要从表中排除哪些属性,这会更好,因为我通常需要类的大部分或全部属性),但是我不知道如何(我从未使用过反射,但我猜就是这样)。此外,该方法应该接受任何类型的类的列表。
Any clever ideas on how to accomplish this?
关于如何做到这一点的任何聪明的想法?
采纳答案by L.B
Maybe something like this?
也许是这样的?
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--
--版本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()repeatedly can be performance killer in a tight loop. It can be better to cache it in a dictionary .
PS:fxn.Compile()在紧密循环中重复调用可能是性能杀手。最好将它缓存在字典中。
回答by Tim S.
Here are two approaches, one using reflection:
这里有两种方法,一种使用反射:
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");
Or using lambdas:
或者使用 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);
With the lambdas, what's happening is you're passing methods to the GetMyTablemethod to get each property. This has benefits over reflection like strong typing, and probably performance.
使用 lambdas,发生的事情是您将方法传递给GetMyTable方法以获取每个属性。这比反射有好处,比如强类型,可能还有性能。
回答by VirDeus
This is what I did and it seems to work fine and not a huge performance hit.
这就是我所做的,它似乎工作正常,并没有造成巨大的性能损失。
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>";
}
To use it just pass the ToHtmlTable() a List Example:
要使用它,只需向 ToHtmlTable() 传递一个列表示例:
List documents = GetMyListOfDocuments(); var table = documents.ToHtmlTable();
列出文件 = GetMyListOfDocuments(); var table = documents.ToHtmlTable();
回答by Cemal Can AKGüL
Good luck with
祝你好运
Extention Method
扩展方法
public static class EnumerableExtension
{
public static string ToHtmlTable<T>(this IEnumerable<T> list, List<string> headerList, List<CustomTableStyle> customTableStyles, params Func<T, object>[] columns)
{
if (customTableStyles == null)
customTableStyles = new List<CustomTableStyle>();
var tableCss = string.Join(" ", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Table).Where(w => w.ClassNameList != null).SelectMany(s => s.ClassNameList)) ?? "";
var trCss = string.Join(" ", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Tr).Where(w => w.ClassNameList != null).SelectMany(s => s.ClassNameList)) ?? "";
var thCss = string.Join(" ", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Th).Where(w => w.ClassNameList != null).SelectMany(s => s.ClassNameList)) ?? "";
var tdCss = string.Join(" ", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Td).Where(w => w.ClassNameList != null).SelectMany(s => s.ClassNameList)) ?? "";
var tableInlineCss = string.Join(";", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Table).Where(w => w.InlineStyleValueList != null).SelectMany(s => s.InlineStyleValueList?.Select(x => String.Format("{0}:{1}", x.Key, x.Value)))) ?? "";
var trInlineCss = string.Join(";", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Tr).Where(w => w.InlineStyleValueList != null).SelectMany(s => s.InlineStyleValueList?.Select(x => String.Format("{0}:{1}", x.Key, x.Value)))) ?? "";
var thInlineCss = string.Join(";", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Th).Where(w => w.InlineStyleValueList != null).SelectMany(s => s.InlineStyleValueList?.Select(x => String.Format("{0}:{1}", x.Key, x.Value)))) ?? "";
var tdInlineCss = string.Join(";", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Td).Where(w => w.InlineStyleValueList != null).SelectMany(s => s.InlineStyleValueList?.Select(x => String.Format("{0}:{1}", x.Key, x.Value)))) ?? "";
var sb = new StringBuilder();
sb.Append($"<table{(string.IsNullOrEmpty(tableCss) ? "" : $" class=\"{tableCss}\"")}{(string.IsNullOrEmpty(tableInlineCss) ? "" : $" style=\"{tableInlineCss}\"")}>");
if (headerList != null)
{
sb.Append($"<tr{(string.IsNullOrEmpty(trCss) ? "" : $" class=\"{trCss}\"")}{(string.IsNullOrEmpty(trInlineCss) ? "" : $" style=\"{trInlineCss}\"")}>");
foreach (var header in headerList)
{
sb.Append($"<th{(string.IsNullOrEmpty(thCss) ? "" : $" class=\"{thCss}\"")}{(string.IsNullOrEmpty(thInlineCss) ? "" : $" style=\"{thInlineCss}\"")}>{header}</th>");
}
sb.Append("</tr>");
}
foreach (var item in list)
{
sb.Append($"<tr{(string.IsNullOrEmpty(trCss) ? "" : $" class=\"{trCss}\"")}{(string.IsNullOrEmpty(trInlineCss) ? "" : $" style=\"{trInlineCss}\"")}>");
foreach (var column in columns)
sb.Append($"<td{(string.IsNullOrEmpty(tdCss) ? "" : $" class=\"{tdCss}\"")}{(string.IsNullOrEmpty(tdInlineCss) ? "" : $" style=\"{tdInlineCss}\"")}>{column(item)}</td>");
sb.Append("</tr>");
}
sb.Append("</table>");
return sb.ToString();
}
public class CustomTableStyle
{
public CustomTableStylePosition CustomTableStylePosition { get; set; }
public List<string> ClassNameList { get; set; }
public Dictionary<string, string> InlineStyleValueList { get; set; }
}
public enum CustomTableStylePosition
{
Table,
Tr,
Th,
Td
}
}
Using
使用
private static void Main(string[] args)
{
var dataList = new List<TestDataClass>
{
new TestDataClass {Name = "A", Lastname = "B", Other = "ABO"},
new TestDataClass {Name = "C", Lastname = "D", Other = "CDO"},
new TestDataClass {Name = "E", Lastname = "F", Other = "EFO"},
new TestDataClass {Name = "G", Lastname = "H", Other = "GHO"}
};
var headerList = new List<string> { "Name", "Surname", "Merge" };
var customTableStyle = new List<EnumerableExtension.CustomTableStyle>
{
new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Table, InlineStyleValueList = new Dictionary<string, string>{{"font-family", "Comic Sans MS" },{"font-size","15px"}}},
new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Table, InlineStyleValueList = new Dictionary<string, string>{{"background-color", "yellow" }}},
new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Tr, InlineStyleValueList =new Dictionary<string, string>{{"color","Blue"},{"font-size","10px"}}},
new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Th,ClassNameList = new List<string>{"normal","underline"}},
new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Th,InlineStyleValueList =new Dictionary<string, string>{{ "background-color", "gray"}}},
new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Td, InlineStyleValueList =new Dictionary<string, string>{{"color","Red"},{"font-size","15px"}}},
};
var htmlResult = dataList.ToHtmlTable(headerList, customTableStyle, x => x.Name, x => x.Lastname, x => $"{x.Name} {x.Lastname}");
}
private class TestDataClass
{
public string Name { get; set; }
public string Lastname { get; set; }
public string Other { get; set; }
}
Result
结果
<table class="normal underline" style="font-family:Comic Sans MS;font-size:15px;background-color:yellow">
<tr style="color:Blue;font-size:10px">
<th style="background-color:gray">Name</th>
<th style="background-color:gray">Surname</th>
<th style="background-color:gray">Merge</th>
</tr>
<tr style="color:Blue;font-size:10px">
<td style="color:Red;font-size:15px">A</td>
<td style="color:Red;font-size:15px">B</td>
<td style="color:Red;font-size:15px">A B</td>
</tr>
<tr style="color:Blue;font-size:10px">
<td style="color:Red;font-size:15px">C</td>
<td style="color:Red;font-size:15px">D</td>
<td style="color:Red;font-size:15px">C D</td>
</tr>
<tr style="color:Blue;font-size:10px">
<td style="color:Red;font-size:15px">E</td>
<td style="color:Red;font-size:15px">F</td>
<td style="color:Red;font-size:15px">E F</td>
</tr>
<tr style="color:Blue;font-size:10px">
<td style="color:Red;font-size:15px">G</td>
<td style="color:Red;font-size:15px">H</td>
<td style="color:Red;font-size:15px">G H</td
</tr>

