如果 viewmodel1 属性发生变化,通知 viewmodel2
Posted
技术标签:
【中文标题】如果 viewmodel1 属性发生变化,通知 viewmodel2【英文标题】:notify viewmodel2 if viewmodel1 property changes 【发布时间】:2016-11-18 07:18:30 【问题描述】:我在 wpf +mvvm 中有一个场景,即如果我的特定属性在 viewmodel1 中发生变化,那么我想通知 viewmodel2 具有可观察的集合,属性“A”已被更改 1)我想为特定财产而不是全部解雇它。
我尝试了下面的代码但没有工作。请让我知道我是如何做到这一点的。
public class Model1 : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
// Create custom event
public event EventHandler NotifyChange;
private string testProperty;
public string TestProperty
get
return testProperty;
set
testProperty = value;
// If changing properties, fire your OnPropertyChanged to update UI
OnPropertyChanged("TestProperty");
private void OnPropertyChanged(string propName)
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
// Fire your custom event if a property changed
NotifyChange(this, null);
public class Model2 : INotifyCollectionChanged
public event NotifyCollectionChangedEventHandler CollectionChanged;
public Model2()
// Assuming there is an accessible instance of model1
Model1 m1Instance = new Model1();
// Hook up your NotifyChange event from model1
m1Instance.NotifyChange += Model1Changed;
private void Model1Changed(object sender, EventArgs e)
// this will be triggered on change in model1
private void OnCollectionChanged(object singleObject)
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, singleObject));
【问题讨论】:
那么,您是说当您在 Model1 上设置属性 TestProperty 时,不会调用 Model2 中的事件处理程序? @TimothyGhanem 我到底想做的是,当我在 viwmodel1 中的属性发生变化时,我想在 viewmodel2 中重新绑定我的可观察集合..... 为什么不让Model2继承自ObservableCollection,然后从Model1绑定到UI呢? @stylishCoder :确保两个 VM 都实现 INotifyPropertyChanged。订阅其他视图模型实例的 PropertyChanged 事件。这就是您识别属性更改所需要做的一切。 我可以建议您研究一下 PubSub 事件的某种事件聚合器吗?如果您要遵循正确的 MVVM 实践,这将是您的最佳选择。如果可以的话,我建议使用 Prism。以下是更多信息:c-sharpcorner.com/UploadFile/5ffb84/… 【参考方案1】:使用 PubSub 事件
我的建议是调查PubSub
事件。
我的建议是使用 Prism。以下是更多信息:http://www.c-sharpcorner.com/UploadFile/5ffb84/prism-event-aggregator-in-wpf-with-mvvm/
在这种情况下,您将坚持正确的 MVVM 做法。
这是 MSDN 非常有用的指南:https://msdn.microsoft.com/en-us/library/ff649664.aspx
对它的工作原理以及如何使用/实现它有一个很好的阅读。
或者
这可行,但如果可能,我仍会推迟使用 PubSub 事件。
你可以试试这个:
public class Model1 : INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private string _property;
public string Property
get return _property;
set
_property = value;
OnPropertyChanged("Property");
private void OnPropertyChanged(string propertyName)
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
public class Model2
public Model2()
// You might be storing your Model1 as a property in the Model2?
// I don't know, but I've put it in the constructor just for example.
var model1 = new Model1();
model1.PropertyChanged += OnModel1PropertyChanged;
private void OnModel1PropertyChanged(object sender, PropertyChangedEventArgs e)
if (e.PropertyName == "Property")
// Do stuff here when the right property has changed in model 1
例如,我在 Model2
的构造函数中只有 new
的 Model1
- 您可能正在分配它并将其作为字段或属性存储在 Model2
ViewModel 的其他地方。
如果您在 ViewModel 中有 ViewModel(父 VM > 子 VM),这可能特别有用。
我经常使用父 > 子 VM,我不认为这违反了 MVVM 最佳实践,但我仍然使用EventAggregator
,而不是event
s。
附带说明,如果您使用的是 C#6:
使用nameof(Property)
代替“魔术字符串”(例如"Property"
)。这使得重构更容易,编译器可以告诉你错误——但本质上是做同样的工作。在您的set
ter 中的OnPropertyChanged()
调用中使用它
查看属性名时也可以使用nameof
关键字,原理同上。像这样:if (e.PropertyName == nameof(Model1.Property)) ...
使用空传播:PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
。将您的 OnPropertyChanged
的方法主体更改为一个不错的单行,同时仍然做同样的工作
我离题了
我会始终尽可能坚持正确的 MVVM 实践。
我每天都使用 Prism 的 EventAggregator
,我会发誓。
仔细阅读 PubSub Events(您可以使用任何类型的事件聚合器,但我会说最好使用 Prism 的)
希望这会有所帮助! :)
【讨论】:
当我点击 tab1 中的任何按钮时尝试通知 tab 2 时,我遇到了一个问题。您能提供任何解决方案吗? 任何按钮?您是使用ICommand
s(或DelegateCommand
s,如果您使用Prism's)来处理您的按钮点击,还是使用btn1.Click += ...
委托处理程序?
我正在使用delegatecommand,当它被触发时,我想调用tab2 viewmodel.....我无法调用......请让我知道如何在这里调用tab2方法???
与 PubSub 事件相同的主体。在命令的处理程序中,您需要使用事件聚合器发布事件。在 tab2 VM 中,您需要确保在 ViewModel 加载/初始化/其他时订阅该事件,并在它从事件聚合器接收事件时创建一个处理程序。【参考方案2】:
如果这只是为了通知 Model2,我认为您不需要所有这些实现。你可以做类似的事情
public string TestProperty
get
return testProperty;
set
testProperty = value;
// If changing properties, fire your OnPropertyChanged to update UI
OnPropertyChanged("TestProperty");
//Here you can call a method of Model2 sating that its changed
Model2 m2Instance = new Model2();
m2Instance.ValueChanged();
在模型 2 中添加方法 ValueChanged。
【讨论】:
但问题来了,我们只对一个属性进行硬编码,不是吗? 您在问题中提到,例如您更改特定属性时。所以我想你的要求只是这样。 ok np @Joseph 我到底想做的是,当我在 viwmodel1 中的属性发生变化时,我想在 viewmodel2 中重新绑定我的可观察集合【参考方案3】:那是因为您没有注册到PropertyChanged
。您正在NotifyChange
上注册您的事件处理程序,因此Model1
中的PropertyChanged
是NULL
,因此不会触发NotifyChange
。
因此,您需要按照以下方式实现您的OnPropertyChanged
:
private void OnPropertyChanged(string propName)
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
if (NotifyChange != null)
NotifyChange(this, null);
最后,如果您希望针对特定属性触发 NotifyChange
,则在触发事件之前调整上面的代码以检查 propName
。
【讨论】:
你能编辑上面的代码以便我更好地理解吗? @Timothy,我能否建议您更好地调用事件。当您进入null
时,总是有可能检查PropertyChanged
实际上可能变成null
。像这样:var handler = PropertyChanged; if (handler != null) handler(this, new ...);
。更好的是,在 C#6 中更容易使用 null 传播:PropertyChanged?.Invoke(this, new ...);
以上是关于如果 viewmodel1 属性发生变化,通知 viewmodel2的主要内容,如果未能解决你的问题,请参考以下文章
vue的computed如果没有出现在模板里面,当它依赖的响应式属性发生变化,getter会触发吗?
vue的computed如果没有出现在模板里面,当它依赖的响应式属性发生变化,getter会触发吗?
Knockout JS 订阅 2 个 observables 的方式是,如果 2 个 observables 中的任何一个发生变化,我只会收到一次通知