如何从包装ViewModel中获取Model实例



短版本:

如果我有ViewModel,包含它的Model对象并公开它的属性,那么在编辑模型后,我如何"取回"它?如果ViewModel中的Model是公共的,则它违反了封装,如果它是私有的,则我无法获取它(对吗?)。


更长版本:

我正在实现一个显示对象集合的应用程序的一部分。假设对象的类型为Gizmo,它在Model层中声明,并且只持有属性并处理自己的序列化/反序列化。

在Model层中,我有一个Repository<T>类,用于处理MasterGizmoDetailGizmo的集合。此存储库类的属性之一是IEnumerable<T> Items { get; },其中T将是Gizmo子类型的一部分。

现在,由于Gizmo没有实现INPC,我在ViewModel层中创建了以下类:

  • GizmoViewModel,其封装Gizmo的每个公共属性,使得设置任何属性都相应地引发PropertyChanged

  • [**]RepositoryViewModel<T>,它有一个ObservableCollection<GizmoViewModel>,其CollectionChanged由一个处理向存储库添加、删除和更新的方法侦听。

请注意,Model层有一个"Repository of Models",而ViewModel层有"ViewModel with an ObservableCollection of ViewModels"。

这个疑问与上述[**]部分有关。我的RepositoryViewModel.CollectionChangedHandler方法如下:

    void CollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
    {
        switch (e.Action)
        {
            case NotifyCollectionChangedAction.Add:
                foreach (var added in e.NewItems)
                {
                    var gvm = added as GizmoViewModel;
                    if (gvm != null)
                    {
                         //// IS ANY OF THE ALTERNATIVES BELOW THE RIGHT ONE?
                         // Gizmo g = gvm.RetrieveModel();          ?? proper getter ??
                         // Gizmo g = GetModelFromViewModel(gvm);   ?? external getter ??
                         // Gizmo g = gvm.Model;                    ?? public model property ??
                        _gizmo_repository.Add(g);
                    }
                }
                break;
            ....

除此之外,如果有人能在这里检测到MVVM的气味,我很乐意知道。

我们甚至可以在View和ViewModel层之外处理我们的模型,所以让模型从ViewModel公开访问我认为是可以接受的。

假设您正在"DataLayer"中创建模型,您可以将模型的实例传递给ViewModel。为了说明我的观点:

///Models ////////////////////////////
public interface IGizmo{}
public class Gizmo:IGizmo{}
public class SuperGizmo : IGizmo {}
public class SuperDuperGizmo : IGizmo { }
//////////////////////////////////////
public interface IGizmoViewModel<out T>
{
    T GetModel();
}
public abstract class GizmoViewModelBase : IGizmoViewModel<IGizmo>
{
    protected GizmoViewModelBase(IGizmo model)
    {
        _Model = model;
    }
    private readonly IGizmo _Model;
    public IGizmo GetModel()
    {
        return _Model;
    }
}
public class GizmoViewModel : GizmoViewModelBase
{
    public GizmoViewModel(Gizmo model)
        : base(model) { }
}
public class SuperDuperGizmoViewModel : GizmoViewModelBase
{
    public SuperDuperGizmoViewModel(SuperDuperGizmo model)
        : base(model){}
}

只要您传递了相同的实例,您的模型存储库就会根据从ViewModel获得的任何更新进行更新。因此,不需要有ViewModels的存储库来获取更新。

阅读您的代码,我认为ViewModel和Model的分离有些混乱。

所以,据我所知,当你的GizmoViewModel的ObservableCollection发生变化时,你正试图将新项目的Gizmo实例添加回你的模型中?

我会以不同的方式处理这个问题。您应该在模型层中创建Gizmo实例,当您这样做时,您应该将其添加到存储库中。

否则,你没有提供足够的信息——或者更确切地说,你提供了太多,但这是错误的信息。您需要描述要执行此操作的情况,创建这些GizmoViewModel的位置等。

从这里可以看出,GizmoViewModelRepository<T>有依赖关系,所以为什么不在创建视图模型时传入存储库呢?

public class GizmoViewModel
{
    private IRepository<Gizmo> _Repo;
    //Underlying model (Doesn't implement INotifyPropertyChanged)
    private Gizmo _Model;
    //Wrapping properties
    public int MyProperty
    {
        get { return _Model.Property; }
        set
        {
            _Model.Property = value;
            NotifyOfPropertyChange();
        }
    }
    ...
    public GizmoViewModel(IRepository<Gizmo> repo)
    {
        _Repo = repo;
    }
    public void AddToRepo()
    {
        _Repo.Add(_Model);
    }
    ...

如果这些方法在RepositoryViewModel基类中,则会更好。你真的可以在这里疯狂地继承遗产。也许是这样的:

var gvm = added as IRepositoryViewModel;
if (gvm != null)
    gvm.AddToRepo();

然后,当您需要将视图模型的底层模型添加到存储库时,您可以简单地调用AddToRepo

也许不是最优雅的解决方案,但是,如果封装是让你担心的问题,那么你需要确保你的依赖关系得到正确管理。

"如果ViewModel中的Model是公共的,则它违反了封装"

您上面的断言是完全错误的,正在扼杀您的代码
通过将ViewModel中的Model属性设置为private,您将被迫重复自己(代码气味),因为您需要在ViewModel中定义与Model相同的属性,从而有效地将其转换为一个Model类,该类模拟了它应该向视图公开的Model。

在MVVM中,ViewModel的角色是为视图提供它所需的所有演示数据和逻辑,并确保模型是这些数据的基本部分,通过将其隐藏在视图中,您将扼杀MVVM。

最新更新