如何从其包装的 ViewModel 中获取 Model 实例?
How do I get the Model instance from inside its wrapping ViewModel?
简短版本:
If I have ViewModel, containing its Model object and exposing its properties, how do I get the model "back" after it has been edited? If the Model-inside-ViewModel is public, it violates encapsulation, and if it is private, I cannot get it (right?).
更长的版本:
我正在实现显示对象集合的应用程序的一部分。假设对象的类型为 Gizmo
,它在模型层中声明,并简单地保存属性并处理其自身的 serialization/deserialization.
在模型层,我有一个Repository<T>
class,我用它来处理MasterGizmo
和DetailGizmo
的集合。此存储库 class 的属性之一是 IEnumerable<T> Items { get; }
,其中 T
将是某些 Gizmo
子类型。
现在由于 Gizmo
没有实现 INPC,我在 ViewModel
层中创建了以下 classes:
GizmoViewModel
,它包装了 Gizmo
的每个 public 属性,这样设置任何 属性 都会引发 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 气味,我将很高兴知道。
据我所见,您的 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
基础 class 中, 会更好。你真的可以在这里疯狂继承。也许是这样的:
var gvm = added as IRepositoryViewModel;
if (gvm != null)
gvm.AddToRepo();
当您需要将视图模型的基础模型添加到存储库时,您只需调用 AddToRepo
。
也许这不是最优雅的解决方案,但是如果封装是您所担心的,那么您需要确保您的依赖关系得到妥善管理。
阅读您的代码,我认为您的 ViewModel 和 Model 分离有些混乱。
因此,据我了解,当您的 GizmoViewModel 的 ObservableCollection 发生变化时,您是否正在尝试将新项目的 Gizmo 实例添加回您的模型?
我会以不同的方式处理这个问题。您应该在您的模型层中创建您的 Gizmo 实例,并且当您这样做时,您应该将它添加到存储库中。
否则,您没有提供足够的信息 - 或者更确切地说,您提供的信息太多了,但这是错误的信息类型。您需要描述您想要执行此操作的情况,创建这些 GizmoViewModel 的位置等。
"If the Model-inside-ViewModel is public, it violates encapsulation"
你上面的断言是完全错误的,并且正在杀死你的代码。
通过将 ViewModel 中的模型 属性 设置为私有,您将被迫重复自己(代码味道),因为您需要在 ViewModel 中定义与您为模型所做的相同的属性,有效地将其转换为一个模型 class 模仿它应该暴露给视图的模型。
在 MVVM 中,ViewModel 的作用是为 View 提供它需要的所有表示数据和逻辑,并且确保 Model 是这些数据的基本部分,通过将它从 View 中隐藏你正在杀死 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 获得的任何更新进行更新。因此,无需 ViewModel 存储库即可获取更新。
简短版本:
If I have ViewModel, containing its Model object and exposing its properties, how do I get the model "back" after it has been edited? If the Model-inside-ViewModel is public, it violates encapsulation, and if it is private, I cannot get it (right?).
更长的版本:
我正在实现显示对象集合的应用程序的一部分。假设对象的类型为 Gizmo
,它在模型层中声明,并简单地保存属性并处理其自身的 serialization/deserialization.
在模型层,我有一个Repository<T>
class,我用它来处理MasterGizmo
和DetailGizmo
的集合。此存储库 class 的属性之一是 IEnumerable<T> Items { get; }
,其中 T
将是某些 Gizmo
子类型。
现在由于 Gizmo
没有实现 INPC,我在 ViewModel
层中创建了以下 classes:
GizmoViewModel
,它包装了Gizmo
的每个 public 属性,这样设置任何 属性 都会引发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 气味,我将很高兴知道。
据我所见,您的 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
基础 class 中, 会更好。你真的可以在这里疯狂继承。也许是这样的:
var gvm = added as IRepositoryViewModel;
if (gvm != null)
gvm.AddToRepo();
当您需要将视图模型的基础模型添加到存储库时,您只需调用 AddToRepo
。
也许这不是最优雅的解决方案,但是如果封装是您所担心的,那么您需要确保您的依赖关系得到妥善管理。
阅读您的代码,我认为您的 ViewModel 和 Model 分离有些混乱。
因此,据我了解,当您的 GizmoViewModel 的 ObservableCollection 发生变化时,您是否正在尝试将新项目的 Gizmo 实例添加回您的模型?
我会以不同的方式处理这个问题。您应该在您的模型层中创建您的 Gizmo 实例,并且当您这样做时,您应该将它添加到存储库中。
否则,您没有提供足够的信息 - 或者更确切地说,您提供的信息太多了,但这是错误的信息类型。您需要描述您想要执行此操作的情况,创建这些 GizmoViewModel 的位置等。
"If the Model-inside-ViewModel is public, it violates encapsulation"
你上面的断言是完全错误的,并且正在杀死你的代码。
通过将 ViewModel 中的模型 属性 设置为私有,您将被迫重复自己(代码味道),因为您需要在 ViewModel 中定义与您为模型所做的相同的属性,有效地将其转换为一个模型 class 模仿它应该暴露给视图的模型。
在 MVVM 中,ViewModel 的作用是为 View 提供它需要的所有表示数据和逻辑,并且确保 Model 是这些数据的基本部分,通过将它从 View 中隐藏你正在杀死 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 获得的任何更新进行更新。因此,无需 ViewModel 存储库即可获取更新。