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

时间:2015-10-28 12:09:18

标签: wpf mvvm repository-pattern observablecollection

简短版本:

  

如果我有ViewModel,包含其Model对象并公开其属性,如何在编辑后“返回”模型?如果Model-inside-ViewModel是公共的,它会违反封装,如果它是私有的,我就无法得到它(对吧?)。

更长的版本:

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

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

既然Gizmo没有实现INPC,我在ViewModel层创建了以下类:

  • GizmoViewModel,其中包含Gizmo的每个公共财产,以便设置任何属性相应地引发PropertyChanged;

  • [**] RepositoryViewModel<T>,其ObservableCollection<GizmoViewModel>由处理存储库的添加,删除和更新的方法监听CollectionChanged

请注意,Model层有一个“模型库”,而ViewModel层有一个“带有ObservableCollection ViewModel的ViewModel”。

怀疑与上述[**]部分有关。我的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气味,我会很高兴知道。

4 个答案:

答案 0 :(得分:1)

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

所以,据我所知,当您的GizmoViewModel的ObservableCollection发生变化时,您是否尝试将新项目的Gizmo实例添加回模型?

我会采用不同的方法。您应该在Model层中创建Gizmo实例,当您执行此操作时,应将其添加到Repository。

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

答案 1 :(得分:1)

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

假设您正在&#34; DataLayer&#34;中创建模型。您可以将Model的实例传递给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获得的任何更新进行更新。因此,无需拥有ViewModel存储库即可获取更新。

答案 2 :(得分:0)

从我在此处看到的情况来看,您的GizmoViewModel依赖于Repository<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

也许不是最优雅的解决方案,但如果封装是令您担心的,那么您需要确保您的依赖关系得到妥善管理。

答案 3 :(得分:0)

“如果Model-inside-ViewModel是公共的,则违反了封装”

您的断言完全错误并且正在查杀您的代码 通过将ViewModel中的Model属性设置为private,您将被迫重复自我(代码气味),因为您需要在ViewModel中定义与您为Model所做的相同的属性,从而有效地将其转换为Model类,模仿它应该向View公开的模型。

在MVVM中,ViewModel角色是为View提供所需的所有表示数据和逻辑,并确保模型是此数据的基本组成部分,方法是将其隐藏在您正在查杀MVVM的视图中。