MVVM and INotifyPropertyChanged Issue
本文关键字:Issue INotifyPropertyChanged and MVVM | 更新日期: 2023-09-27 18:08:20
我对MVVM设计有一个很大的问题。我试图抓住我的内部嵌套对象的每一个PropertyChanged,包括他们的嵌套对象的进一步propertchanged,在我的ViewModel,但我不知道如何做到这一点。
下面是我的结构:
class MyVM
{
public MyVM()
{
this.SomeData = new SomeData();
this.SomeData.NestedObj = new MyNestedDat();
this.SomeData.Str = "This tiggers propertychanged inside MyDat class";
// this triggers propertychanged event inside MyNestedDat class
this.SomeData.NestedObj.Num = 123;
}
// and here should be a method where i catch all possibe propertychanges from my nested objets and their nested objets, how do i do that?
public MyDat SomeData
{
get;
set;
}
}
class MyDat : INotifyPropertyChanged
{
private string str;
public string Str;
{
get { return this.str;}
set
{
this.str = value;
this.PropertyChanged(this, "Str");
}
}
publicMyNestedDat NestedObj
{
get;
set;
}
}
class MyNestedDat : INotifyPropertyChanged
{
private int num;
public int Num
{
get{ return this.num;}
set
{
this.num = value;
this.PropertyChanged(this, "Num");
}
}
}
我如何得到这个工作?我真的不知道从哪里开始。
mynestedat类抛出PropertyChanged, MyDat类抛出PropertyChanged,我想在我的视图模型中捕获它们。我怎么能做到呢?
在我看来,你所问的问题有一些概念上的错误。想象一下,您得到了一个适用于您的场景的解决方案(您很满意),并考虑以下内容:
- 如果添加另一层会发生什么?你还期望它能像以前一样工作吗?
- 属性变化是否应该传播(
viewModel1.propA
通知viewModel2.PropA
)? - 属性变化是否应该转换(
viewModel1.SomeProp
通知ViewModel2.AnotherProp
)? - 是否关注性能?如果您需要将属性更改事件传播到多个级别,那么这将如何执行?
这应该敲响警钟,当前的方法不是正确的。
你需要的是以松耦合的方式在你的视图模型之间提供通信,这样你的视图模型甚至不需要知道彼此的存在。这样做的美妙之处在于,它不仅可以用于属性更改,还可以用于其他情况。
对于属性更改事件的情况,一个viewModel想要知道何时发生(记住,它可能不是属性更改事件)。这意味着另一个viewModel需要某种方式说"嘿,一个属性已经改变了"(或"我的状态已经改变了","数据库调用已经完成"等)。
现在在c#中,您可以提供提供此功能的事件....除了,现在你的对象知道彼此,这给你留下了你以前遇到过的同样的问题。
为了克服这个问题,您需要另一个对象,一个中介(在本例中我们称之为Messenger
),它的唯一目的是处理在对象之间传递的消息,以便它们可以相互忽略。
总体思路是这样的。在提供通知的viewModel中,你可以这样做:
public string MyProp
{
get { return _myProp; }
set
{
_mProp = value;
OnPropertyChanged("MyProp");
Messenger.PostMessage(new VMChangedMessage { ViewModel = this, PropertyName = "MyProp" });
}
}
在对事件感兴趣的viewModel中你可以这样做:
public class ViewModel2
{
public ViewModel2()
{
Messenger.Subscribe<VMChangedMessage>(handleMessage);
}
private void handleMessage(VMChangedMessage msg)
{
// Do something with the information here...
}
}
注意,这两个viewmodel从不相互引用。它们现在是松耦合的。
有许多预先存在的实现已经可用,并且创建自己的实现并不困难(信使基本上保留对某个消息感兴趣的对象列表,并在需要通知感兴趣的各方时迭代该列表)。有一些事情可以以不同的方式实现(有些实现只是传递字符串消息,而不是将信息封装在对象中,有些实现自动处理观察者的清理)。
我建议使用Josh Smiths(优秀的)MVVM Foundation,其中包括一个信使类。它也是开源的,所以你可以看到它是如何工作的。
对于PropertyName
应该在PropertyChangedEventArgs中包含什么没有明确的约束。
参见订阅INotifyPropertyChanged获取嵌套(子)对象。
下面是一个例子:
class A : BaseObjectImplementingINotifyPropertyChanged {
private string m_name;
public string Name {
get { return m_name; }
set {
if(m_name != value) {
m_name = value;
RaisePropertyChanged("Name");
}
}
}
}
class B : BaseObjectImplementingINotifyPropertyChanged {
private A m_a;
public A A {
get { return m_a; }
set {
if(m_a != value) {
if(m_a != null) m_a.PropertyChanged -= OnAPropertyChanged;
m_a = value;
if(m_a != null) m_a.PropertyChanged += OnAPropertyChanged;
RaisePropertyChanged("A");
}
}
}
private void OnAPropertyChanged(object sender, PropertyChangedEventArgs e) {
RaisePropertyChanged("A." + e.PropertyName);
}
}
B b = new B();
b.PropertyChanged += (s, e) => { Console.WriteLine(e.PropertyName); };
b.A.Name = "Blah"; // Will print "A.Name"
最好将Model和ViewModel的概念分开。
通过使用比Model
平坦的ViewModel
对象,可以避免这种情况。使用自动映射工具,如Automapper,然后允许您将Model
映射到ViewModel
,反之亦然。
class MyDatViewModel : INotifyPropertyChanged
{
public string Str
{
// ... Get Set
}
public int NestedObjNum
{
// ... Get set
}
}
// Configure AutoMapper
Mapper.CreateMap<MyDat, MyDatViewModel>();
// Perform mapping
MyDatViewModel viewModel = Mapper.Map<MyDat, MyDatViewModel>(someData);