实现自己的ObservableCollection

3

在我的WPF项目中,我需要一个可观察的集合,它始终保持正确的顺序。我的想法是使用一个SortedSet<T>并实现自己的AddAndNotifyRemoveAndNotify方法。在这些方法中,我将调用NotifyPropertyChanged,如下所示:

public class ObservableSortedSet<T> : SortedSet<T>, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }

    public void AddAndNotify(T element)
    {
        Add(element);
        NotifyPropertyChanged(...);
    }

    public void RemoveAndNotify(T element)
    {
        Remove(element);
        NotifyPropertyChanged(...);
    }
}

但是哪个属性(或属性)会是那个呢?
如何实现一个集合,使UI在集合内容更改时更新?
还是说直接在我的ViewModel中使用SortedSet有更简单的方法?
编辑:
我不想使用预定义的ObservableCollection与排序视图一起使用。我知道可以使用CollectionViewSource或转换器来实现这一点,但这些解决方案并不吸引我。对于CollectionViewSource无法处理的分层数据,我认为转换器版本是对CollectionViewSource限制的可怕解决方法。我想使用一个干净的解决方案。
因此,这个问题不是如何对ObservableCollection进行排序的重复。我不想对ObservableCollection进行排序,我想使用一个SortedSet,它可以告诉UI变化。

可能是如何对ObservableCollection进行排序的重复问题。 - ASh
你可以重写InsertItem方法。 - Wouter
3个回答

6
您应该实现INotifyCollectionChanged接口,而不是使用解释。请参阅INotifyCollectionChanged接口文档了解更多信息。
public class ObservableSortedSet<T> : SortedSet<T>, INotifyPropertyChanged, INotifyCollectionChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public void NotifyPropertyChanged(string propName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }

    public void AddAndNotify(T element)
    {
        Add(element);
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
    }

    public void RemoveAndNotify(T element)
    {
        Remove(element);
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove));
    }
}

这看起来很有前途。之前不知道INotifyCollectionChanged,谢谢!但是当我调用RemoveAndNotify时,我遇到了运行时错误。它说NotifyCollectionChangedEventArgs的构造函数仅支持操作Reset,不知道那是什么意思... - Kjara
MSDN表示 new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, element) 是有效的(参见这里:https://msdn.microsoft.com/en-us/library/ms653207(v=vs.110).aspx)。但我仍然会收到关于索引的运行时错误...怎么回事? - Kjara
@Kjara,它是否特别提到了索引?我认为没有SomethingUnspecifiedAboutAnIndexException异常。 - 15ee8f99-57ff-4f92-890c-b56153
@Kjara 哦,明白了。那个 KeyedCollection 的解决方案对你是否有用?我想知道你是否也可以给你的类添加“假”索引。 - 15ee8f99-57ff-4f92-890c-b56153
显示剩余2条评论

3

我用以下方法成功实现:

public class ObservableSortedSet<T> : SortedSet<T>, INotifyCollectionChanged where T : IComparable<T>
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public new void Add(T element)
    {
        base.Add(element);
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public override void Clear()
    {
        base.Clear();
        CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }

    public new void Remove(T element)
    {
        base.Remove(element);
        CollectionChanged?.Invoke(this,
            new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}

为什么我总是将 Reset 操作作为 EventArg?

  • Remove 的原因:如果集合没有索引,似乎无法利用它(请参见 在没有索引的集合上实现 INotifyCollectionChangedObservable Stack 和 Queue - 在这两种情况下,问题归结为无法指定正确的索引,导致运行时错误。看看所有评论中的答案说“索引 xyz 对我不起作用!”)

  • Add 的原因:内部上,集合是有序的,但 UI 没有正确反映出来。我的猜测是,通过给 EventArg new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, element);,GUI 只会被通知新元素应该被添加到末尾。

根据 https://msdn.microsoft.com/en-us/library/system.collections.specialized.notifycollectionchangedaction(v=vs.100).aspxReset 表示“集合的内容发生了重大变化。”对于 SortedSet 来说,这有点正确,因为它的内部结构可能会因为只添加或删除一个元素而发生大量变化。所以,也许我的解决方案甚至不是一种变通方法(即使感觉像是一种)!


{btsdaf} - NWoodsman

0
你可以使用CollectionViewSource:
<Window.Resources>
    <CollectionViewSource x:Key="yourViewSource" source="{Binding YourCollection}"/>
</Window.Resources>

然后,按照你需要的属性进行排序:

CollectionViewSource yourViewSource=
            ((CollectionViewSource)(this.FindResource("yourViewSource")));
yourViewSource.SortDescriptions.Add(new SortDescription("SortByProperty", ListSortDirection.Descending));

将其绑定为您的ItemSource:

ItemsSource="{Binding Source={StaticResource yourViewSource}}"

不行,CollectionViewSource 不支持分层数据 - 或者说,如果它支持的话,我还没有找到利用它的方法(而且 StackOverflow 上有很多关于这个问题的提问!)。 - Kjara

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接