在没有索引的集合上实现INotifyCollectionChanged

本文关键字:实现 INotifyCollectionChanged 集合 索引 | 更新日期: 2023-09-27 17:58:26

在ASP.Net独家工作了几年后,我现在对WPF感到非常兴奋。我目前遇到的问题是,我有一个自定义集合类,需要将其绑定到列表框。除了从集合中删除一个项目外,似乎一切都正常。当我尝试时,我得到了错误:“Collection Remove event must specify item position.”问题是这个集合不使用索引,所以我看不到指定位置的方法,到目前为止,谷歌还没有向我展示一个可行的解决方案…

该类被定义为实现ICollection<>INotifyCollectionChanged。我的内部项容器是一个Dictionary,它使用项的Name(字符串)值作为键。除了这两个接口定义的方法之外,此集合还有一个索引器,允许通过Name访问项,并重写ContainsRemove方法,以便也可以使用项Name调用它们。这适用于"添加和编辑",但在尝试删除时引发上述异常。

以下是相关代码的摘录:

class Foo
{
    public string Name
    {
        get;
        set;
    }
}
class FooCollection : ICollection<Foo>, INotifyCollectionChanged
{
    Dictionary<string, Foo> Items;
    public FooCollection()
    {
        Items = new Dictionary<string, Foo>();
    }
    #region ICollection<Foo> Members
    //***REMOVED FOR BREVITY***
    public bool Remove(Foo item)
    {
        return this.Remove(item.Name);
    }
    public bool Remove(string name)
    {
        bool Value = this.Contains(name);
        if (Value)
        {
            NotifyCollectionChangedEventArgs E = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, Items[name]);
            Value = Items.Remove(name);
            if (Value)
            {
                RaiseCollectionChanged(E);
            }
        }
        return Value;
    }
    #endregion
    #region INotifyCollectionChanged Members
    public event NotifyCollectionChangedEventHandler CollectionChanged;
    private void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (CollectionChanged != null)
        {
            CollectionChanged(this, e);
        }
    }
    #endregion
}

在没有索引的集合上实现INotifyCollectionChanged

您的自定义集合似乎是KeyedCollection<TKey,TItem>的重新发明,它在内部使用字典,具有索引。如果TKey是基于intint的枚举,则int索引的索引器可能会被隐藏,但这是可以修复的。

关于使KeyedCollection与WPF一起工作,我发现了这篇文章,在这篇文章中,他基本上通过实现INotifyCollectionChanged并重写SetItem()InsertItem()ClearItems()RemoveItem()来生成ObservableKeyedCollection<TKey,TItem>,同时添加AddRange()并将Func<TItem,TKey>传递给构造函数,以便从TItem获得TKey

需要一点间接,但您可以使用Linq。不包括错误处理,你可以这样做:

var items = dict.Keys.Select((k, i) => new { idx = i, key = k });
var index = items.FirstOrDefault(f => f.key == name).idx;

同样,只要保持一致,就可以使用值而不是关键帧。

因此,我通过将remove事件更改为重置来进行临时破解,并着手处理代码的其他部分。当我回到这个问题时,我发现/意识到SortedList类将满足我的要求,并允许我正确地实现CollectionChanged事件,只需对现有代码进行最小的更改。

对于那些不熟悉这门课的人(我以前从未使用过),这里有一个基于我迄今为止所读内容的快速总结。在大多数方面,它看起来像一本字典,尽管内部结构不同。此集合维护键和值的排序列表,而不是哈希表。这意味着在将数据放入和取出集合时会有更多的开销,但其内存消耗较低。这种差异的显著程度似乎取决于需要存储的数据量以及密钥使用的数据类型。

由于我在这个实例中的数据量相对较低,并且我需要让列表框中的项目按照它们的名称值进行排序,所以在我的情况下,使用这个类似乎是一个很好的答案。如果有人对为什么不应该使用这个类有意见,请告诉我。

感谢所有人的建议和评论,希望这个帖子能帮助其他人。

我能够将NotifyCollectionChangedAction.Replace操作与空的NewItems列表一起使用,以成功引发非索引集合的CollectionChanged事件。