将项目动态添加到其数据源绑定到集合的ListBox

时间:2018-08-25 19:35:42

标签: c# wpf generics reflection

我正在尝试创建自定义可重用的WPF UserControl,该控件具有一个列表框和一个用于添加新行的按钮,就像DataGrid允许用户添加新行一样。

此控件将绑定到各种视图模型(例如List,ObservableCollection等)中的许多不同类型的集合。

我有一个带有依赖属性的UserControl,该依赖属性称为IEnumerable类型的DataSource(必须具有IEnumerable才能允许Obse​​rvableCollections,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的也是如此,所以我知道这并非不可能。我只是看不到如何使其工作。我将不胜感激。

1 个答案:

答案 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);