模型更改时刷新 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 的所有属性的数据绑定的好方法的主要内容,如果未能解决你的问题,请参考以下文章

.Net ViewModel 在页面刷新时不更新

如何从 ViewModel 更改样式属性?

Usercontrol - 从 ViewModel 通知属性更改

ajax搜索结果返回后初始化viewmodel并刷新

在点击按钮时更改视图的 ViewModel 会导致:“AttributeGraph:通过属性警告检测到循环”

MVVMCross 通知 ViewModel Model 属性的变化