我正在尝试创建自定义可重用的WPF UserControl,该控件具有一个列表框和一个用于添加新行的按钮,就像DataGrid允许用户添加新行一样。
此控件将绑定到各种视图模型(例如List,ObservableCollection等)中的许多不同类型的集合。
我有一个带有依赖属性的UserControl,该依赖属性称为IEnumerable类型的DataSource(必须具有IEnumerable才能允许ObservableCollections,List等)。我想为基础集合中的任何对象创建一个新实例,并将其添加到绑定到ItemsSource的原始集合中。
我创建了一个方法,该方法将创建集合所包含的对象的新实例:
private object GetNewItem()
{
if (ItemsSource == null)
throw new ArgumentException("ItemsSource not set");
Type itemType = null;
foreach (Type i in ItemsSource.GetType().GetInterfaces())
{
if (i.IsGenericType && i.GetGenericTypeDefinition().Equals(typeof(IEnumerable<>)))
itemType = i.GetGenericArguments()[0];
}
if (itemType == null)
throw new ArgumentException("Unable to get ItemsSource's Type T");
return Activator.CreateInstance(itemType);
}
现在,我只需要将新对象添加到原始集合中即可。不幸的是,IEnumerable不允许添加项目,因为它不是易变的。
我可以检测到最初使用的是哪种类型的收藏集,即:
if (itemsType.IsGenericType && itemsType.GetGenericTypeDefinition() == typeof(ObservableCollection<>))
因此,我可以获取集合的类型以及该集合的通用类型(即,集合是“ ObservableCollection <>”,而通用类型是“ Person”),但是我不知道如何将其回退。 ..即使我可以,也无法将GetNewItem的结果添加为:
((ObservableCollection<Person>) ItemsSource).Add(object)
...如果不将对象投射到“人”,将无法正常工作。
DataGrids能够添加它的ItemsSource的基础类型的新实例,即使它的ItemsSource是IEnumerable的也是如此,所以我知道这并非不可能。我只是看不到如何使其工作。我将不胜感激。
答案 0 :(得分:1)
DataGrid借助{ItemsSource}的IEditableCollectionView
视图添加新项目:请参见source code
所以尝试将ItemsSource强制转换为IEditableCollectionView并添加新项,而无需自己创建实例。
var c = CollectionViewSource.GetDefaultView(ItemsSource) as IEditableCollectionView;
if (c != null && c.CanAddNew)
c.AddNew();
但是,即使该列表具有Add方法,该方法也无法与List<DateTime>
一起使用:CanAddNew为false。也许可以通过事实来解释,DateTime是一个不变的结构。
此方法似乎有限,我可以尝试另一种方法:
没有任何IEnumerable具有Add
方法,该方法在IList接口中定义。如果ItemsSource不是IList,则没有关于具体类型的特殊知识就不能添加任何内容。而且即使使用IList也无法保证(它可以是ReadOnly或FixedSize(数组,[]
))
var c = ItemsSource as IList;
if (c != null && !c.IsReadOnly && !c.IsFixedSize)
c.Add(newInstance);