我想制作一个通用方法来卸载 pdf 中的表格。
大纲方法:
static PdfPTable printTable<T>(IEnumerable<ColumnDefinition> columns, IEnumerable<T> rows, Font font)
{
var table = new PdfPTable(columns.Count());
foreach (var row in rows)
{
foreach (var column in columns)
{
var value = column.Property.GetValue(row, null);
System.Diagnostics.Debug.WriteLine(value as Enumerable);
if (value is IEnumerable<DTO.StateReason> nestedRows)
{
table.AddCell(new PdfPCell(printTable(nestedRows.GetColumnDefinitions(), nestedRows, font)) { Padding = 0});
}
else
{
var textContent = string.Empty;
if (value != null && !string.IsNullOrEmpty(column.Format))
{
switch (column.TypeName)
{
case nameof(DateTime):
{
textContent = ((DateTime)value).ToString(column.Format);
break;
}
case nameof(Int16):
case nameof(Int32):
case nameof(Int64):
{
textContent = ((int)value).ToString(column.Format);
break;
}
case nameof(Decimal):
{
textContent = ((decimal)value).ToString(column.Format);
break;
}
default:
break;
}
}
else if(value != null)
{
textContent = value.ToString();
}
table.AddCell(new PdfPCell(new Phrase(textContent, font)));
}
}
}
return table;
}
方法实现GetColumnDefinition
static IEnumerable<ColumnDefinition> GetColumnDefinitions<T>(this IEnumerable<T> source)
{
try
{
var index = 0;
var result = (from property in typeof(T).GetProperties().OrderBy(x => x.Name)
let columnAttribute = property.GetCustomAttributes(typeof(ColumnAttribute), true).SingleOrDefault() as ColumnAttribute
let formatAttribute = property.GetCustomAttributes(typeof(DisplayFormatAttribute), true).SingleOrDefault() as DisplayFormatAttribute
let ignoreAttribute = property.GetCustomAttributes(typeof(IgnoreAttribute), true).SingleOrDefault() as IgnoreAttribute
where ignoreAttribute == null
select new ColumnDefinition
{
Property = property,
Name = columnAttribute?.Name ?? property.Name,
TypeName = columnAttribute?.TypeName ?? string.Empty,
Format = formatAttribute?.DataFormatString ?? string.Empty,
Order = columnAttribute?.Order ?? index++
})
.OrderBy(x => x.Order);
return result;
}
catch (Exception e)
{
throw;
}
}
将表格打印为 pdf 的示例类
public class ActHistoryList
{
[Services.Exports.Ignore]
public int Id { get; set; }
[Services.Exports.Ignore]
public int StateId { get; set; }
[Column("Состояние", Order = 0)]
public string State { get; set; }
[Column(Order = 1)]
public IList<StateReason> Reasons { get; set; } = new List<StateReason>();
[Column("Дата/Время", Order = 2, TypeName = nameof(DateTime)), DisplayFormat(DataFormatString = "dd.MM.yyyy HH:mm")]
public DateTime Timestamp { get; set; }
[Column("Автор", Order = 3)]
public string Author { get; set; }
}
public class StateReason
{
[Services.Exports.Ignore]
public int Id { get; set; }
[Column("Основание", Order = 1)]
public string Name { get; set; }
[Column("Комментарий", Order = 2)]
public string Description { get; set; }
}
代码可以运行,如果值值是数组、集合等,我不知道如何实现递归调用,在代码if(value is IEnumerable<DTO.StateReason> nestedRows)中它DTO.StateReason可以是任何类。
如果问题是可以有任何类而不是 DTO.StateReason ,那么您可以用's
IEnumerable<DTO.StateReason>替换IEnumerable并获取旧的 .NET 2.0 版本。object如果您对问题的答案感兴趣,那么为了使属性可迭代,它的类型必须实现
IEnumerator GetEnumerator();.因此,借助反射(Reflection),您可以获得对象的类型(
GetType),并枚举其方法(GetMethods)以寻找正确的方法。或者检查它是否实现了IEnumerable:接口typeof(IEnumerable).IsAssignableFrom(prop.PropertyType)。无论如何,尝试在泛型之上构建泛型代码总是很痛苦。从好的方面来说,值得使用Expression或ILGenerator编译 lambda 表达式并调用它来抵消反射引入的开销。