您可以通过调用ItemsSource.GetType().GetInterfaces(),找到IEnumerable<T> 接口(任何泛型集合都将实现)的Type 对象,并在其上调用GetGenericArguments() 来获取要创建的类型。 IEnumerable<T> 当然有一个类型参数,所以这是您创建实例所需的类型。
然后you can create an instance fairly easily(请参阅下面的更新以了解将这一切包装成单个方法调用的静态方法):
ObjectType instance = (ObjectType)Activator.CreateInstance("AssemblyName",
"MyNamespace.ObjectType");
您需要声明类型的程序集but that's a property of Type。 Assembly 也有 a CreateInstance method。这是做同样事情的另一种方法:
Type otype = typeof(ObjectType);
ObjectType instance = (ObjectType)otype.Assembly.CreateInstance(otype.FullName);
如果要实例化的类型没有默认构造函数,这会变得更丑陋。您必须编写显式代码来提供值,并且无法保证它们有意义。但至少这比一堆IPOCOFactory 实现给消费者带来的负担要轻得多。
请记住System.String 没有默认构造函数。使用List<String> 测试下面的代码是很自然的,但这会失败。
获得ItemsSource 中的对象类型后,您可以通过以编程方式枚举属性的名称和类型以及自动生成列来进一步简化维护。如果需要,您可以编写一个Attribute 类来控制显示哪些,提供显示名称等。
更新
这是一个粗略的实现,它可以让我创建在不同程序集中声明的类的实例:
/// <summary>
/// Collection item type must have default constructor
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
public static Object CreateInstanceOfCollectionItem(IEnumerable items)
{
try
{
var itemType = items.GetType()
.GetInterfaces()
.FirstOrDefault(t => t.Name == "IEnumerable`1")
?.GetGenericArguments()
.First();
// If it's not generic, we may be able to retrieve an item and get its type.
// System.Windows.Controls.DataGrid will auto-generate columns for items in
// a non-generic collection, based on the properties of the first object in
// the collection (I tried it).
if (itemType == null)
{
itemType = items.Cast<Object>().FirstOrDefault()?.GetType();
}
// If that failed, we can't do anything.
if (itemType == null)
{
return null;
}
return itemType.Assembly.CreateInstance(itemType.FullName);
}
catch (Exception ex)
{
return null;
}
}
public static TestCreate()
{
var e = Enumerable.Empty<Foo.Bar<Foo.Baz>>();
var result = CreateInstanceOfCollectionItem(e);
}
如果你愿意,你可以让CreateInstanceOfCollectionItem() 成为IEnumerable 的扩展方法:
var newItem = ItemsSource?.CreateInstanceOfCollectionItem();
注意
这取决于实际集合是通用集合,但它不关心您对集合的reference 类型。 ItemsControl.ItemsSource 属于System.Collections.IEnumerable 类型,因为任何标准的泛型集合都支持该接口,因此可以转换为它。但是在该非泛型接口引用上调用 GetType() 将返回引用另一端(可以说)对象的实际真实运行时类型:
var ienumref = (new List<String>()) as System.Collections.IEnumerable;
// fullName will be "System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"
// ...or something like it, for whatever version of .NET is on the host.
var fullName = ienumref.GetType().Name;