短版本:
如果我有ViewModel,包含它的Model对象并公开它的属性,那么在编辑模型后,我如何"取回"它?如果ViewModel中的Model是公共的,则它违反了封装,如果它是私有的,则我无法获取它(对吗?)。
更长版本:
我正在实现一个显示对象集合的应用程序的一部分。假设对象的类型为Gizmo
,它在Model层中声明,并且只持有属性并处理自己的序列化/反序列化。
在Model层中,我有一个Repository<T>
类,用于处理MasterGizmo
和DetailGizmo
的集合。此存储库类的属性之一是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的位置等。
从这里可以看出,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
。
也许不是最优雅的解决方案,但是,如果封装是让你担心的问题,那么你需要确保你的依赖关系得到正确管理。
"如果ViewModel中的Model是公共的,则它违反了封装"
您上面的断言是完全错误的,正在扼杀您的代码
通过将ViewModel中的Model属性设置为private,您将被迫重复自己(代码气味),因为您需要在ViewModel中定义与Model相同的属性,从而有效地将其转换为一个Model类,该类模拟了它应该向视图公开的Model。
在MVVM中,ViewModel的角色是为视图提供它所需的所有演示数据和逻辑,并确保模型是这些数据的基本部分,通过将其隐藏在视图中,您将扼杀MVVM。