将ObservableCollection<T>包装为IObservableCollection<ViewModel<T>>

3
在C#/WPF中,提供一个实现IObservableCollection<ViewModel<T>>接口并包装模型中的ObservableCollection<T>的类是最佳方式吗?
模型可以向底层集合添加/删除T元素,或者视图可以从视图模型层添加/删除ViewModel元素。
从MVVM模式的角度来看,这种方法是否有问题?是否有包含此类或类似类的库?

一个只包含类型为ObservableCollection<MyModel>或者ObservableCollection<MyViewModel<MyModel>>属性的类有什么问题吗?我从来没有理解为什么人们总是想要编写继承自ObservableCollection的东西...它就像一个带有更改通知的List。你不会重写List<T>来创建数据类,也不应该重写ObservableCollection<T>(除非是为了扩展列表/集合的功能)。 - Rachel
@Rachel 我有一个列表视图,其中每个项目都是 ItemModel,我需要将它们中的每一个都包装在 ItemViewModel 中。在 MVVM 模式中,模型不应直接暴露给视图。 - dbkk
1
“MVVM-Purist”方法是从不将模型暴露给视图,但往往以这种方式更为实用,因此两种方法都是有效的。即使您想坚持该设计模式,我仍然不明白为什么要覆盖ObservableCollection。您的ViewModel应该包含一个类型为ObservableCollection<SomeViewModel>的属性,而SomeViewModel应该公开SomeModel的属性。 - Rachel
1
只要在将数据发送到服务器之前验证数据,我认为View看到一些Model部分并没有太大问题。 - Jake Berger
@jberger 当然,有多种方法可以解决问题,但我想知道从MVVM的角度来看,哪种方法是可取的。 - dbkk
在我看来,MVVM的优点是将视图与模型解耦,从而允许前端和后端同时进行设计。ViewModel仅通过绑定将Model暴露给View。 - Jake Berger
1个回答

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;
    }
}

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