模型 List<T> 和视图模型 ObservableCollection<T> 会出现重复数据吗?

10
在业务层中有一个需要在UI上显示的List<T>,最佳实践是什么?我目前在viewmodel中使用数据绑定和ObservableCollection<T>,它复制了List<T>的数据。显然的问题是当业务层中的List<T>被修改时,ObservableCollection<T>需要重新创建,以便反映List<T>的更改。这不能是最好的方法。
我也不接受在业务层中使用ObservableCollection<T>作为答案。
谢谢!

谁、何时以及如何修改了您模型中的 List<T> - Federico Berasategui
1
我正在寻找一种解决方案,可以在编程或UI中修改列表,而不需要复制数据。 - ryand
编程上如何实现?从模型本身?从VM中?请提供更多细节。 - Federico Berasategui
假设列表可以从视图模型或模型进行修改。 - ryand
2个回答

8
  1. 如果您坚持使用List<T>并分别通知列表修改事件,那么复制是唯一明智的方法。

    1. 如果您有没有详细说明实际更改内容的ListChanged事件,则可以避免重复,只需在代理集合中包装列表,该代理集合实现了INotifyCollectionChanged接口,将以NotifyCollectionChangedAction.Reset模式触发适当的CollectionChanged事件。

    2. 如果您有粒度更细的ItemChangedItemAdded等事件,则实际上是在复制ObservableCollection<T>的功能。在这种情况下,您可以在代理集合中包装应用程序,该代理集合实现了INotifyCollectionChanged接口,但可以理解您的架构,并将事件转换为适当的NotifyCollectionChangedAction

  2. 在业务层中使用ObservableCollection<T>并不是一个坏主意。它是一种专门的集合,提供了通知项更改的公共接口,而不是为WinForms或WPF或其他特定设计的类。


2
谢谢,我想我一直以来有个固定的观念,认为除非直接与UI打交道,否则使用ObservableCollection都是不好的。在使用ObservableCollection时,是否存在任何明显的可扩展性缺陷? - ryand
2
@Rydel 请查看问题在WPF MVVM应用程序的服务层中使用ObservableCollection,它有高质量的答案。 - Athari

3

您可以实现INotifyCollectionChanged接口,但如果您想以一种方式使用它,即在实现自己的类时可以保留集合,则实现IEnumerable的类也将为您完成大部分工作。例如,以下是我用于保存所有将被更新的集合的基类,在这个实现中还考虑了一个排序问题,变量_ordering:

public abstract class BaseINotifyCollectionChanged<T, K> : INotifyCollectionChanged, IEnumerable<T>
{
    Func<T, K> _ordering;
    bool _ascending;

    public BaseINotifyCollectionChanged()
    {
    }

    public BaseINotifyCollectionChanged(Func<T, K> ordering, bool ascending = true)
    {
        _ordering = ordering;
        _ascending = ascending;
        OnCollectionChanged();
    }

    protected abstract IList<T> GetCollection();

    public event NotifyCollectionChangedEventHandler CollectionChanged;
    protected void OnCollectionChanged()
    {
        if (CollectionChanged != null)
            CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
    public void RaiseCollectionChanged()
    {
        OnCollectionChanged();
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _ordering == null ? GetCollection().GetEnumerator() : _ascending ? GetCollection().OrderBy<T, K>(_ordering).GetEnumerator() :
                                                                                  GetCollection().OrderByDescending<T, K>(_ordering).GetEnumerator();
    }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _ordering == null ? GetCollection().GetEnumerator() : _ascending ? GetCollection().OrderBy<T, K>(_ordering).GetEnumerator() :
                                                                                  GetCollection().OrderByDescending<T, K>(_ordering).GetEnumerator();
    }
}
}

当您拥有这个实现后,您可以根据自己的需要使用它,并在您的应用程序中使用所有必要的集合。如果您正在为自己工作,这里是一个使用这个抽象类的示例:

public class Categories : BaseINotifyCollectionChanged<Category, string>
{
    long _onCategoryRoot;
    public void SetOnCategoryRoot(long categoryId)
    {
        _onCategoryRoot = categoryId;
        RaiseCollectionChanged();
    }

    protected override IList<Category> GetCollection()
    {
        Category category = new Category();
        return _onRoot ? category.GetRootCategories() : category.GetSubCategoriesOnRoot(_onCategoryRoot);
    }
}

当你在类中设置一个_onCategoryRoot,你将通过RaiseCollectionChanged()方法更新所显示的集合,因此你需要在你的视图模型中添加一个带有类Categories的属性,并在XAML中进行绑定。

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