如何在ViewModel层次结构中冒泡更改?

我的MainView.xaml包含我的SmartForm视图:

   

SmartForm视图加载ItemsControl

    

这是DataTypeViews的ObservableCollection:

 List formFields = new List(); formFields.Add(new FormField { IdCode = "firstName", Label = "First Name", Value = "Jim" }); formFields.Add(new FormField { IdCode = "lastName", Label = "Last Name", Value = "Smith" }); formFields.Add(new FormField { IdCode = "address1", Label = "Address 1", Value = "123 North Ashton Rd." }); formFields.Add(new FormField { IdCode = "address2", Label = "Address 2", Value = "Box 23434" }); formFields.Add(new FormField { IdCode = "city", Label = "City", Value = "New Haven" }); formFields.Add(new FormField { IdCode = "state", Label = "State", Value = "NM" }); formFields.Add(new FormField { IdCode = "zip", Label = "Zip Code", Value = "34234" }); foreach (FormField formField in formFields) { DataTypeView dtv = new DataTypeView(); DataTypeViewModel dtvm = new DataTypeViewModel(formField); dtv.DataContext = dtvm; DataTypeViews.Add(dtv); } 

每个视图显示构建表单的标签和文本框:

            

如何将DataTypeViewModel中发生的文本框更改冒泡到SmartFormViewModel中?

或者换句话说: 如果ViewModel A包含ViewModel B的集合,并且在ViewModel B中发生了更改,那么如何将该更改冒泡到ViewModel A?

您可以让父虚拟机连接到子虚拟机上的PropertyChanged事件。 这是一种PITA,用于跟踪已添加/删除的子项等,因此您可以考虑将子VM存储在ItemObservableCollection

 public sealed class ItemObservableCollection : ObservableCollection where T : INotifyPropertyChanged { public event EventHandler> ItemPropertyChanged; protected override void InsertItem(int index, T item) { base.InsertItem(index, item); item.PropertyChanged += item_PropertyChanged; } protected override void RemoveItem(int index) { var item= this[index]; base.RemoveItem(index); item.PropertyChanged -= item_PropertyChanged; } protected override void ClearItems() { foreach (var item in this) { item.PropertyChanged -= item_PropertyChanged; } base.ClearItems(); } protected override void SetItem(int index, T item) { var oldItem = this[index]; oldItem.PropertyChanged -= item_PropertyChanged; base.SetItem(index, item); item.PropertyChanged -= item_PropertyChanged; } private void item_PropertyChanged(object sender, PropertyChangedEventArgs e) { OnItemPropertyChanged((T)sender, e.PropertyName); } private void OnItemPropertyChanged(T item, string propertyName) { ItemPropertyChanged.Raise(this, new ItemPropertyChangedEventArgs(item, propertyName)); } } 

然后,您的父虚拟机可以通过以下方式监听子项的所有更改:

 _formFields.ItemPropertyChanged += (s, e) => Foo(); 

我认为你应该使用你可以在这里阅读的中介模式。

基本上它是一个静态类,允许ViewModels(或任何类)相互通信并来回传递参数。

基本上,ViewModel A开始监听某种消息类型(例如ViewModelBChanged),并且只要该事件发生,ViewModelB只是通知任何正在监听此消息类型的人,它也可以传递它想要的任何信息。

这是调解员的骨架。

 public static class MyMediator { public static void Register(Action callback, string message); public static void NotifyColleagues(string message, object args); } 

ViewModel A会这样做(可能在构造函数中):

 MyMediator.Register(ProcessMessage,"ViewModelBChanged") 

然后必须声明一个这样的函数:

 void ProcessMessage(object args) { //Do some important stuff here } 

只要想要告诉ViewModel A,ViewModel B就会调用它

 MyMediator.NotifyColleagues("ViewModelBChanged",this); 

中介类将负责调用viewModel A的回调函数。然后每个人都很高兴。

我个人喜欢把这些字符串消息值放在这样的静态类中

 static class MediatorMessages { public static string ViewModelBChanged= "ViewModelBChanged"; } 

这样你就可以做到以下(而不是上面的):

  MyMediator.Register(ProcessMessage,MediatorMessages.ViewModelBChanged) MyMediator.NotifyColleagues(MediatorMessages.ViewModelBChanged,this); 

如果这不清楚只是谷歌MVVM中介并点击你的心脏内容:)

非WPF方式是在DataTypeViewModel上创建静态事件。 这允许您在适当时(在属性设置器或属性更改处理程序中)从DataTypeViewModel触发事件。 当然,您还必须在SmartForm中注册事件的监听器(要求SmartForm了解DataTypeViewModel类型)。

或者,我认为您可以创建自己的自定义路由事件。

虽然肯特就在上面,但并非所有子视图模型的变化都与属性有关,有些可能比这更具语义性。 在这种情况下,实施责任链模式的变体可能是一个很好的选择。

简而言之

  • 使所有子视图模型都知道“主处理程序对象”,该对象具有处理各种更改事件的方法。 与该对象的通信可以通过事件或消息进行,具体取决于更改的复杂程度。
  • 让这个主处理程序对象注册一个处理变量事件的处理程序对象的集合,每个变量事件对应一个。 它们可以像原始模式一样链接,也可以在快速集合(例如字典)中注册以获得性能。
  • 使此处理程序对象向已注册的处理程序分派适当的更改。

“master”处理程序不必是Singleton,其注册表可以依赖于父视图模型本身。

希望它足够清楚(抱歉不放代码)

我通过将ViewModel本身传递到其中包含的ViewModel中来解决这个问题, 这是一个演示如何完成的演示 。