从View中访问Model类,无需直接访问Model Layer

我需要在我的一个Model类型的用户控件中创建一个Property ,但我认为我必须阻止View Layer 直接访问 Model Layer View Layer

我有一个View ModelView Model ,它提供了我的模型对象集

  • SetOfA_UserControl
  • SetOfA_ViewModel
  • 一个模型

我需要一个属性在我的用户控件中这样:

 public A_Model SelectedA { get; set; } 

一种方法是创建一个新的View Model如下面的代码,并在我的User Control使用它:

 // ------------ View Model Layer ------------ public class SingleA_ViewModel: ModelA { } // --------------- View Layer --------------- public SingleA_ViewModel SelectedA { get; set; } 

但我试图阻止一个新的空视图模型类inheritance上面的模型。 这是对的吗?

您有什么建议阻止直接访问Model layer并在我的User Control创建Property ???


编辑1:

我有3个项目:

  • 查看项目
  • 查看模型项目
  • 示范项目

我想知道我可以阻止在View project引用Model project吗?

我的View Model中也有一个SelectedA属性,我将我的逻辑放在View model class ,它在我的视图中运行良好, 我在UserControl中有一个SelectedA属性,它将它绑定到我的ViewModel类中的SelectedA属性…但是我需要直接访问UserControl Model来定义这个属性!

当我从View直接访问Model ,我的代码是这样的:

 // ------------ Model Layer ------------ public class AModel { } // ------------ View Model Layer ------------ public class SetOfA_ViewModel: INotifyPropertyChanged { public AModel SelectedA { get; set; } public ObservableCollection Items { get { return _items; } set { _items = value; OnPropertyChanged("Items"); } } // Other Logic codes to fill and keep SelectedA value and.... } // --------------- View Layer --------------- public partial class MyUserControl : UserControl { public AModel SelectedA { get { return (AModel)GetValue(SelectedAProperty); } set { var oldValue = (AModel)GetValue(SelectedAProperty); if (oldValue != value) SetValue(SelectedAProperty, value); } } public static readonly DependencyProperty SelectedAProperty = DependencyProperty.Register( "SelectedA", typeof(AModel), typeof(MyUserControl), new PropertyMetadata(OnSelectedAValueChanged)); public MyUserControl () { InitializeComponent(); const string NAME_OF_PROPERTY_IN_VM = "SelectedA"; var binding = new Binding(NAME_OF_PROPERTY_IN_VM) { Mode = BindingMode.TwoWay }; this.SetBinding(SelectedAProperty, binding); } private static void OnSelectedAValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) { //------ } } 

上面的方法适合我,我现在使用它但是我试图删除任何直接访问和我的View Project Model Project引用然后如何在我的user control实现AModelSelectedA依赖属性?

有些朋友说你可以直接从view项目访问模型项目。 我想知道Correct ways ,而不是 possible ways ……!


编辑2

当我在我的用户控件中保留SelectedA项时,然后在我的窗口中使用它,如下所示:

   

编辑3

为什么我要阻止从ViewModel直接访问Model?

我搜索了MVVM图,但没有找到并直接从View访问模型。 所有图表都说:

在此处输入图像描述

………. 在此处输入图像描述

现在我们可以从视图直接访问模型了吗?

  • 为什么有很多样本可以直接访问网络上的模型?

  • 为什么有些人说你可以这样做?

  • 如果我们能够做到这一点并直接访问模型是一个正确的实现,为什么在上图中View和Model之间没有任何关系?

视图模型不是模型,因此不应该派生。

如果您决定将视图耦合到模型,则模型中的任何更改(通常由外部源指定)都可能会影响其使用的视图。

对每个视图使用视图模型时,某些视图可能不会受到更改的影响,可以通过调整视图或在视图模型中编写代码来修复受影响的视图。

是的,在中间添加一个额外的图层是额外的工作,但它也带有从视图到模型再转回的明确点。 经过几次增量后,它可能是值得的。

额外的层还为命令和validation以及查看特定属性提供了一个很好的扩展点。

如果您决定将模型公开为viewmodel的属性,那么向属于视图的模型添加属性和命令会变得非常诱人。 它们会快速污染模型,使模型难以重复使用或重新生成。

没有法律也没有警察。 考虑我的论点并选择一个选项。 尽量开放以便稍后更改您的设计。 现在看似简单的事情可能会在以后变得困难。 我们不知道会发生什么; 敏捷/灵活。 根据我的经验,我发现对于在许多版本中存活的应用程序,在模型和视图模型之间进行清晰分离更有效,但对于短期应用程序而言可能太多了。

在实现MVVM模式时,我总是确保模型不知道或假设有关viewmodel的任何内容,并且viewmodel不知道或假设有关视图的任何内容。 视图模型是中间的人; 它知道在哪里获取模型的实例以及在何处发送模型的更改。 很多时候我使用过一个或多个知道如何获取或保存模型实例的存储库,因此viewmodel只需知道存储库。 存储库可以注意或委托其他function,例如跨viewmodel实例进行缓存。

通常,我为每个视图创建一个viewmodel,并将viewmodel的实例分配给视图的datacontext。 所有绑定路径(对于属性和命令)都与该视图模型相关。

有时我通过向主视图模型添加属性视图模型本身来嵌套视图模型。

“模型的盲接口”:

以下是我避免让View知道Model的方法,同时给了Views一种方法来传递关于正在处理什么模型的知识。
常见场景:在一个页面上,用户选择一个项目,应用程序转到另一个页面,该页面想知道选择了哪个项目。

我的方法是“中间立场”:它允许视图推理和沟通模型状态,而不会暴露除识别模型对象的“盲”接口之外的任何东西。 这是可能的,因为视图模型了解模型,因此可以在需要信息时查询。

在发明这个之前,我们允许View类知道模型类。 在移植到新平台时,它变成了PITA – 复杂的逻辑倾向于在View类中编写。 必须在每个新平台中识别和重新创建该代码 – 这是一个特定于UI和模型特定推理的混乱。 我们有ViewModel类和大多数特定于模型的推理,但在移植时仍然很容易“丢失”一些微妙的逻辑。 现在,模型特定的推理至少需要调用VM。 当代码应该从V移动到VM(代码有许多VM引用)时,很明显。

注意:它包括“IOwner”,(或“IView”可能是更好的名称),这是VM从视图中获取信息的一种方式,例如文本框的内容。 也许正确使用数据绑定会消失,但我还没有掌握它。

这并不是从MVVM的“教科书”实现开始的。 相反,它开始于在一个平台上使用纠结的代码,因为我将越来越多的模型细节从View类移动到(新添加的)VM类中,直到Views不再知道模型的任何细节。 因此View强制调用VM以进行模型的任何操作。 [一旦我完全理解“数据绑定”,我可能会使这段代码更清晰。 我不一定推荐这种方法,但如果你需要迁移现有的非MVVM,非数据绑定代码,你可以通过重构逐步到达这里,而不是从头开始重写。

 // ---------- Views ---------- // TODO: Replace with your platform-specific View base class. internal class View { } // Each View class has access to its ViewModel class, and sometimes to other View classes. // If a View1 needs to alter a ViewModel belonging to another View2, this should be via custom methods on View2, // or perhaps via a limited Interface onto ViewModel2. // Eg, to build the initial state of a View2 that we are segueing to. internal class View1 : View, VM1.IOwner { private VM1 vm; // --- Implementing method of "VM1.IOwner" --- // TODO: Replace this with access to a textbox in your UI. private string _text1; public string Text1 { get { return _text1; } set { _text1 = value; } } // TODO: Replace these with platform-specific methods for page appearing, disappearing. public void OnLoad() { vm = new VM1(this); vm.Load(); } public void OnStore() { vm.Store(); } // Example of "building" the input to a following view. public void OnSegue(View nextView) { var nextView2 = nextView as View2; if (nextView2 != null) nextView2.IncomingA = vm.CurrentA; } // --- represents a method used within view's workflow. --- public void SomeMethod() { // Eg, based on user selection, make a specified ModelA be "current". // (In practice, it would take a more complex scenario for view to want to hold an IModelA.) // (In this simple case, View could hold an index, call SetCurrentModelAByIndex, not need an IModelA object.) IModelA desiredA = vm.GetOneModelA(1); if (desiredA != null) vm.CurrentA = desiredA; else { // ... message to user ... } } } internal class View2 : View //TODO , VM2.IOwner { internal IModelA IncomingA; // ... } // ---------- ViewModels ---------- // Each of these corresponds to a single view. So "VM1" corresponds to "V1". // ViewModel classes have access to Model classes. internal class VM1 { // This gives a limited way for VM1 to "call back" to its V1. // "IView" would be an alternative name for this. internal interface IOwner { string Text1 { get; set; } } private readonly IOwner _owner; private ModelA _a; private IList someAs; internal VM1(IOwner owner) { _owner = owner; } internal void Load() { // TODO: Replace this with logic that is told (or "knows") where to get the active Models. someAs = new List {new ModelA(), new ModelA()}; _a = (someAs.Count > 0 ? someAs[0] : null); _owner.Text1 = (_a != null ? _a.TextA : "No ModelA"); } internal void Store() { // Called by V1 to store user input back to model(s). if (_a != null) _a.TextA = _owner.Text1; } // View is only allowed to know about "IModelA", not "ModelA". internal IModelA GetOneModelA(int index) { if (index < someAs.Count) return someAs[index]; else return null; } // Return "true" if succeeds. internal bool SetCurrentModelAByIndex(int index) { IModelA desiredA = GetOneModelA(index); if (desiredA != null) { CurrentA = desiredA; return true; } else return false; } internal IModelA CurrentA { get { // Note the return type is "IModelA"; this represents the ModelA without giving view direct access to features of A. return _a; } set { // By design, IModelA always holds a ModelA (or null), so this cast always succeeds. _a = (ModelA)value; } } // Shows how view can get value of a specified field of CurrentA. internal string CurrentTextA() { return TextA(_a); } // Shows how view can get value of a specified field of any ModelA. internal string TextA(IModelA ia) { var a = (ModelA)ia; // TBD: Or maybe pass an empty string when missing. return (a != null ? a.TextA : null); } } // ---------- "Blind" Model Interfaces ---------- // This has NO methods. It is used to pass around a "ModelA" WITHOUT exposing its features or class. // It is all that a "View" is allowed to know; must be passed to a VM to act on it. internal interface IModelA { } // ---------- Models ---------- internal class ModelA : IModelA { internal string TextA; } 

如果您想要一个表示SelectedItem的SelectedA,那么这应该在ViewModel因为您不太可能想要在业务逻辑层中访问它。

露出你的财产public A_Model SelectedA { get; set; } public A_Model SelectedA { get; set; } 在ViewModel中(实现INotifyPropertyChanged ,然后将您正在使用的任何集合控件的SelectedItem绑定到此属性。

如果这纯粹用于UI,则直接绑定到XAML中的相关控件。