模型更改时刷新 ViewModel 的所有属性的数据绑定的好方法
Posted
技术标签:
【中文标题】模型更改时刷新 ViewModel 的所有属性的数据绑定的好方法【英文标题】:Good way to refresh databinding on all properties of a ViewModel when Model changes 【发布时间】:2011-06-06 19:01:24 【问题描述】:短版
如果我更新我的 ViewModel 包装的 Model 对象,有什么好方法来为我的 ViewModel 公开的所有模型属性触发属性更改通知?
详细版
我正在开发一个遵循 MVVM 模式的 WPF 客户端,并尝试处理从服务到在我的视图中显示的数据的传入更新。当客户端收到更新时,更新以我用作模型的 DTO 的形式出现。
如果此模型是对视图中显示的现有模型的更新,我希望关联的 ViewModel 更新其数据绑定属性,以便视图反映更改。
让我用一个例子来说明。 考虑我的模型:
class FooModel
public int FooModelProperty get; set;
包装在 ViewModel 中:
class FooViewModel
private FooModel _model;
public FooModel Model
get return _model;
set
_model = value;
OnPropertyChanged("Model");
public int FooViewModelProperty
get return Model.FooModelProperty;
set
Model.FooModelProperty = value;
OnPropertyChanged("FooViewModelProperty");
问题:
当更新的模型到达时,我设置 ViewModel 的 Model 属性,如下所示:
instanceOfFooVM.Model = newModel;
这会导致OnPropertyChanged("Model")
触发,但不会触发OnPropertyChanged("FooViewModelProperty")
,除非我从Model
的设置器中显式调用后者。因此,当我更改模型时,绑定到 FooViewModelProperty
的视图不会更新以显示该属性的新值。
为每个暴露的 Model 属性显式调用 OnPropertyChanged
显然不是一个理想的解决方案,也不是采用 newModel
并遍历其属性以逐个更新 ViewModel 的属性。
对于更新整个模型并需要为其所有公开属性触发更改通知的问题,有什么更好的方法?
【问题讨论】:
【参考方案1】: Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(String.Empty))
对于 VB.net,如果其他人需要它。如果您已经实现了“INotifyPropertyChanged”,那么最后一行就是您所需要的。
【讨论】:
【参考方案2】:只要设置了Model
属性,就订阅它自己的PropertyChanged
事件。当您的处理程序被调用时,触发您自己的 PropertyChanged
事件。当 Model
设置为其他值时,请从旧的 Model
中删除您的处理程序。
例子:
class FooViewModel
private FooModel _model;
public FooModel Model
get return _model;
set
if (_model != null)
_model.PropertyChanged -= ModelPropertyChanged;
if (value != null)
value.PropertyChanged += ModelPropertyChanged;
_model = value;
OnPropertyChanged("Model");
public int FooViewModelProperty
get return Model.FooModelProperty;
set
Model.FooModelProperty = value;
OnPropertyChanged("FooViewModelProperty");
private void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
// Here you will need to translate the property names from those
// present on your Model to those present on your ViewModel.
// For example:
OnPropertyChanged(e.PropertyName.Replace("FooModel", "FooViewModel"));
【讨论】:
【参考方案3】:一种选择是监听您自己的事件,并创建一个辅助例程来根据需要发出其他通知。
这可以像在构造函数中添加一样简单:
public FooViewModel()
this.PropertyChanged += (o,e) =>
if (e.PropertyName == "Model")
OnPropertyChanged("FooViewModelProperty");
// Add other properties "dependent" on Model here...
;
【讨论】:
我希望在答案中看到类似 AOP 的东西,但我真的很喜欢简单。投票赞成:)。也许另一种方法可以只是将所有依赖的 OnPropertyChanged 调用提取到一个单独的方法中,然后从 FooModel 的 setter 中调用它。 @Anvaka:我在内部有一个辅助例程,它通过“AddDepedentProperty”和表达式树来执行此操作......这是一个有用的选项。 感谢这种方法。不过,它的缺点是它保留了必须为我的 ViewModel 公开的所有属性维护一组OnPropertyChanged
调用的问题。例如,当添加属性时,该集合必须保持最新。我相信@Joe White 已经找到了我需要的东西......
@djacobson:这也是一个不错的选择——请注意,它会重做绑定到该对象的每个属性——如果你的虚拟机是一个简单的模型包装器,那就完美了——但如果它包含其他应用程序逻辑,它可以触发比需要更多的通知。
好电话,我一定会记住的!就我而言,我认为这将是一个值得做出的权衡……至少在证明并非如此之前。 :)【参考方案4】:
根据docs:
PropertyChanged 事件可以通过使用 null 或 String.Empty 作为 PropertyChangedEventArgs 中的属性名称来指示对象上的所有属性都已更改。
【讨论】:
我确信我在某个时候读过这篇文章,但我完全忘记了它。当 ViewModel 的整个模型发生变化时,这似乎是最好的方法。非常感谢! 我一直使用这种方法来刷新对象。我不建议在每次属性更改后都这样做,但对于初始对象加载来说效果很好。 如果使用 MVVM-light 会报错。不幸的是,这里没有实际的解决方案,但另请参阅***.com/questions/3554831 我的测试表明我只有 String.Empty 有效。 Null 对我不起作用 @Alon Catz 对,我也尝试使用null
作为 propertyName
值并且没有更新所有类绑定属性,但是使用 string.Empty
作为 propertyName
值 确实 更新所有类绑定属性。以上是关于模型更改时刷新 ViewModel 的所有属性的数据绑定的好方法的主要内容,如果未能解决你的问题,请参考以下文章
Usercontrol - 从 ViewModel 通知属性更改