【问题标题】:IObservableCollection<ViewModel<T>> wrapping ObservableCollection<T>IObservableCollection<ViewModel<T>> 包装 ObservableCollection<T>
【发布时间】:2014-02-06 18:14:19
【问题描述】:

在 C#/WPF 中,提供一个实现 IObservableCollection&lt;ViewModel&lt;T&gt;&gt; 并从模型中环绕 ObservableCollection&lt;T&gt; 的类的最佳方法是什么?

模型可以向底层集合添加/删除 T 元素,或者视图可以从视图模型层添加/删除 ViewModel 元素。

从 MVVM 模式的角度来看,这有什么可疑之处吗?是否有包含这个或类似类的库?

【问题讨论】:

  • 只包含ObservableCollection&lt;MyModel&gt; 甚至ObservableCollection&lt;MyViewModel&lt;MyModel&gt;&gt; 类型属性的类有什么问题?我一直不明白为什么人们总是想写一些继承自ObservableCollection 的东西……就像List,但带有更改通知。你不要覆盖List&lt;T&gt;来创建一个数据类,你也不应该覆盖ObservableCollection&lt;T&gt;(除非它是为了扩展列表/集合的功能)
  • @Rachel 我有一个列表视图,其中每个项目都是ItemModel,我需要将每个项目包装在ItemViewModel 中。在 MVVM 模式中,模型不应该直接暴露给视图。
  • “MVVM-Purist”方法是从不将模型暴露给视图,但通常这样做更实用,因此任何一种方法都有效。即使你想坚持这种设计模式,我仍然不明白你为什么要覆盖ObservableCollection。您的 ViewModel 应该包含 ObservableCollection&lt;SomeViewModel&gt; 类型的属性,而 SomeViewModel 应该公开 SomeModel 的属性
  • @dbkk:只要您在将数据发送到服务器之前验证数据,我认为 View 看到一些模型部件没有太大问题。
  • @jberger 当然,有多种方法可以给猫剥皮,但我想知道从 MVVM 的角度来看哪种方法是可取的。

标签: c# wpf mvvm design-patterns


【解决方案1】:

我在下面编写了一个类,您可以将其用于单线程目的。我只实现了添加和删除,因此您必须实现您可能使用的任何其他操作:...

用法:

var viewModelWrapper = new ObservableCollectionViewModel<ItemViewModel, Model>(Model.List, item => new ItemViewModel(item));

通用类 - 可观察的集合视图模型包装器:

public class ObservableCollectionViewModel<TItemViewModel, TModel> :
    ObservableCollection<TItemViewModel>, IDisposable
    where TItemViewModel : BaseViewModel<TModel>
    where TModel : new()
{
    private readonly ObservableCollection<TModel> models;
    private readonly Func<TModel, TItemViewModel> viewModelConstructor;

    public ObservableCollectionViewModel(ObservableCollection<TModel> models,
        Func<TModel, TItemViewModel> viewModelConstructor)
    {
        this.models = models;
        this.viewModelConstructor = viewModelConstructor;
        CreateViewModelCollection();
        models.CollectionChanged += models_CollectionChanged;
        CollectionChanged += viewModels_CollectionChanged;
    }

    private void viewModels_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        models.CollectionChanged -= models_CollectionChanged;
        try
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    {
                        foreach (var newItem in e.NewItems)
                        {
                            models.Add(((TItemViewModel)newItem).Model);
                        }
                        break;
                    }
                case NotifyCollectionChangedAction.Remove:
                    {
                        foreach (var oldItem in e.OldItems)
                        {
                            models.Remove(((TItemViewModel)oldItem).Model);
                        }
                        break;
                    }
                // TODO: Add missing actions
                default: throw new NotImplementedException();
            }
        }
        finally
        {
            models.CollectionChanged += models_CollectionChanged;
        }
    }

    private void models_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        CollectionChanged -= viewModels_CollectionChanged;
        try
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    {
                        foreach (var newItem in e.NewItems)
                        {
                            Add(viewModelConstructor((TModel)newItem));
                        }
                        break;
                    }
                case NotifyCollectionChangedAction.Remove:
                    {
                        var viewModels = this.Where(viewModel => e.OldItems.Contains(viewModel.Model)).ToList();
                        foreach (var viewModel in viewModels)
                        {
                            Remove(viewModel);
                        }
                        break;
                    }
                // TODO: Add missing actions
                default: throw new NotImplementedException();
            }
        }
        finally
        {
            CollectionChanged += viewModels_CollectionChanged;
        }
    }

    /// <summary>
    /// Only called once, by constructor
    /// </summary>
    private void CreateViewModelCollection()
    {
        foreach (var model in models)
        {
            Add(viewModelConstructor(model));
        }
    }

    public void Dispose()
    {
        models.CollectionChanged -= models_CollectionChanged;
    }
}

【讨论】:

    猜你喜欢
    • 2011-05-15
    • 1970-01-01
    • 2023-04-05
    • 2012-05-28
    • 1970-01-01
    • 1970-01-01
    • 2011-09-09
    • 2010-10-02
    • 1970-01-01
    相关资源
    最近更新 更多