简短版本:
如果我有ViewModel,包含其Model对象并公开其属性,如何在编辑后“返回”模型?如果Model-inside-ViewModel是公共的,它会违反封装,如果它是私有的,我就无法得到它(对吧?)。
更长的版本:
我正在实现显示对象集合的应用程序的一部分。假设对象属于Gizmo
类型,它在Model层中声明,只是保存属性并处理它自己的序列化/反序列化。
在模型层中,我有一个Repository<T>
类,我用它来处理MasterGizmo
和DetailGizmo
的集合。此存储库类的一个属性是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气味,我会很高兴知道。
答案 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的视图中。