C# 从视图访问模型类而不直接访问模型层
我需要在C# 从视图访问模型类而不直接访问模型层,c#,wpf,mvvm,C#,Wpf,Mvvm,我需要在模型类型的一个用户控件中创建一个属性,但我认为我必须阻止直接访问模型层,从视图层 我有一个模型的视图模型,它提供了一组我的模型对象 SetOfA_用户控件 SetOfA_视图模型 A_模型 我的用户控件中需要这样的属性: public A_Model SelectedA { get; set; } 一种方法是创建一个新的视图模型,就像下面的代码一样,并在我的用户控件中使用它: // ------------ View Model Layer ------------ public
模型
类型的一个用户控件中创建一个属性
,但我认为我必须阻止直接访问模型层
,从视图层
我有一个模型的视图模型
,它提供了一组我的模型对象
- SetOfA_用户控件
- SetOfA_视图模型
- A_模型
我的用户控件中需要这样的属性:
public A_Model SelectedA { get; set; }
一种方法是创建一个新的视图模型
,就像下面的代码一样,并在我的用户控件
中使用它:
// ------------ View Model Layer ------------
public class SingleA_ViewModel: ModelA
{
}
// --------------- View Layer ---------------
public SingleA_ViewModel SelectedA { get; set; }
但是我试图阻止一个新的空视图模型类继承上面的模型。对吗
您有什么建议来阻止直接访问模型层
,并在my用户控件中创建属性
编辑1:
我有3个项目:
- 查看项目
- 查看模型项目
- 模型项目
我想知道我是否可以阻止在查看项目中引用模型项目
我的视图模型中也有一个SelectedA
属性,我将我的逻辑放在视图模型类中,它在我的视图中运行得很好,但是我的UserControl
中也有一个SelectedA
属性,我将它绑定到SelectedA
属性在ViewModel
类中。。。但是我需要直接访问UserControl
中的Model
来定义此属性
当我从视图
直接访问模型
时,我的代码如下:
// ------------ Model Layer ------------
public class AModel
{
}
// ------------ View Model Layer ------------
public class SetOfA_ViewModel: INotifyPropertyChanged
{
public AModel SelectedA { get; set; }
public ObservableCollection<AModel> 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)
{
//------
}
}
<userControls:MyUserControl x:Name="MyUserControl1"/>
<Label Content="{Binding ElementName=MyUserControl1, Path=SelectedA.Title}" />
编辑3
为什么要阻止从ViewModel直接访问模型?
我搜索了MVVM图,并没有找到从视图到模型的直接访问。所有图表都显示:
现在我们可以从视图直接访问模型了吗?
- 为什么有许多示例可以直接访问web上的视图中的模型
- 为什么有些人说你能做到
- 如果我们能做到这一点,并且直接访问模型是一个正确的实现,为什么在上面的图中视图和模型之间没有任何关系
如果您想要一个表示SelectedItem的SelectedA
,那么它应该在ViewModel
中,因为您很可能不想在业务逻辑层中访问它
在ViewModel(implementINotifyPropertyChanged
中公开您的属性public A_Model SelectedA{get;set;}
,然后将您正在使用的任何集合控件的SelectedItem
绑定到此属性
如果仅用于UI,则直接绑定到XAML中讨论的控件。viewmodel不是模型,因此不应派生
如果决定将视图与模型耦合,则通常由外部源指定的模型中的任何更改都可能会影响在其中使用的视图
为每个视图使用viewmodel时,某些视图可能根本不受更改的影响,并且可以通过调整视图或在viewmodel中编写代码来修复受影响的视图
是的,在中间添加一个额外的层是额外的工作,但它也有一个从视图到模型再到模型的清晰过渡点。在几个增量之后,它可能是值得的
额外的层还为命令、验证和视图特定属性提供了一个很好的扩展点
如果您决定将模型作为viewmodel的属性公开,则很容易向模型添加视图特定的属性和命令。这些属性和命令会很快污染模型,并使模型难以重用或重新生成
没有法律,也没有警察。考虑到我的论点,选择一个选项。试着在以后开放地改变你的设计。现在看起来容易的事情以后可能会变得困难。我们不知道会发生什么;要灵活/灵活。根据我的经验,我发现对于在许多版本中幸存下来的应用程序来说,更容易在model和viewmodel之间有一个清晰的分离是非常有效的,但对于短期应用程序来说,这可能太过分了
在执行MVVM模式时,我总是确保模型不知道或假设任何关于VIEW模型,并且VIEW模型不知道或假设任何关于视图的视图。VIEW模型是中间的人;它知道在哪里获取模型的实例和<强>哪里发送模型的改变。使用一个或多个知道如何获取或保存模型实例的存储库来获取或保存模型实例,因此viewmodel只需要了解存储库。存储库可以处理或委托其他功能,例如跨viewmodel实例进行缓存
通常,我为每个视图创建一个viewmodel,并将viewmodel的一个实例分配给视图的datacontext。所有绑定路径(属性和命令)都是相对于该viewmodel的
有时,我通过向主viewmodel添加属性来嵌套viewmodels,这些属性本身就是viewmodels。“模型的盲接口”:
以下是我如何避免视图了解模型,同时为视图提供一种方法来传递关于正在处理的模型的知识。
常见场景:在一个页面上,用户选择一个项目,应用程序转到另一个页面,该页面希望知道选择了哪个项目
我的方法是一种“中间地带”:它允许视图对模型状态进行推理和交流,而无需公开除标识模型对象的“盲”界面以外的任何内容。这是可能的,因为viewmodels理解模型,因此在我
// ---------- 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.
// E.g., 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()
{
// E.g., 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<ModelA> 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<ModelA> {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;
}