当一个字段被改变时,MVVM对象火灾属性改变事件

本文关键字:改变 MVVM 火灾 事件 属性 对象 字段 一个 | 更新日期: 2023-09-27 18:16:07

所以我有一个对象的MVVM绑定设置。该对象属于如下的类:

public class SomeClass
{
  public int Field1 {get; set:}
  public List<MyClass> CollectionOfObjects {get; set;}
  public string Description {get; set;}
}

我的MVVM类实现INotifyPropertyChanged…

private SomeClass _viewableObject;
public SomeClass ViewableObject
{
    get
     {
       return _viewableObject;
     }
    set
     {
        _viewableObject = value;
        OnPropertyChanged("ViewableObject");
     }

} 

这在定义新对象的情况下非常有效。例如:

 ViewableObject = new SomeClass();

但是我将UI组件绑定到SomeClass的字段。Field1Description被推到一个文本框中,CollectionOfObjects被推到一个DataGrid中。

<Datagrid .... ItemsSource = "{Binding ViewableObject.CollectionOfObjects}" ></DataGrid>

所以,如果我只更新ViewableObject.CollectionOfObjects,而不是整个ViewableObject,我怎么能让UI更新,以反映修改?是否有一种更清洁的方法来做到这一点,而不是在MVVM中拆分SomeClass的每个字段?

当一个字段被改变时,MVVM对象火灾属性改变事件

你可以创建你自己的集合来实现INotifyCollectionChanged,但是你会发现使用ObservableCollection更容易,因为它已经做到了。

这里有几个选项。其中最简单的是将CollectionOfObjectsList<MyClass>转换为ObservableCollection<MyClass> -正如已经提到的那样。

但是,要注意,这只会在向ObservableCollection添加新项目或删除现有项目时给UI通知。

如果你改变了CollectionOfObjects指向的整个引用,那么使用ObservableCollection是没有用的。相反,您需要在SomeClass上实现INotifyPropertyChanged,并在设置每个属性值时引发PropertyChanged事件。

结果SomeClass看起来像这样:

public class SomeClass : INotifyPropertyChanged
{
    public SomeClass()
    {
        CollectionOfObjects = new ObservableCollection<MyClass>();
    }
    public int Field1 
    {
        get 
        {
            return _field1;
        }
        set
        {
            if(_field1 != value)
            {
                _field1 = value;
                PropertyChanged(new PropertyChangedEventArgs("Field1"));
            }
        }
    }
    public ObservableCollection<MyClass> CollectionOfObjects 
    {
        get 
        {
            return _collectionOfObjects;
        }
        set
        {
            if(_collectionOfObjects != value)
            {
                _collectionOfObjects = value;
                PropertyChanged(new PropertyChangedEventArgs("CollectionOfObjects"));
            }
        }
    }
    public string Description
    {
        get 
        {
            return _description;
        }
        set
        {
            if(_description != value)
            {
                _description = value;
                PropertyChanged(new PropertyChangedEventArgs("Description"));
            }
        }
    }
    public event PropertyChangedEventHandler PropertyChanged = delegate {}
    private int _field1;
    private ObservableCollection<MyClass> _collectionOfObjects;
    private string _description;
}

注意,严格来说,这不再是一个Model对象。它对INotifyPropertyChangedObservableCollection的依赖使其成为ViewModel。但是,你已经有一个了……

选择是你是否可以容忍"污染"你的模型类,以至于它成为一个ViewModel。对于某些场景,这是绝对没问题的。在其他情况下,最好严格分离Model和ViewModel的关注点。此时,您需要引入一个映射器,它能够从Model转换为ViewModel——也可能反过来。也许模型是一个数据库记录,或者完全是其他的东西。它可能需要依赖于其他库,从某些特定于层的基类继承,或者包含大量的领域逻辑。

严格分离Model和ViewModel的另一个好处是,两者可以独立变化。模型可能包含一些UI(以及ViewModel)不需要包含的属性。或者,ViewModel可能会将一些属性聚合为一个平均值,或者一个总数,等等。ViewModels通常应该具有映射到用户界面的属性,而model可能没有。

最后一点。通过引入映射器并正确分离Model和ViewModel,您将如何称呼现有的ViewModel类?我倾向于将其称为控制器。然后,模式就变成了M-V-C-VM:模型-视图-控制器-视图模型。Controller仍然是UI元素的DataContext,但是公开了一个ViewModel属性。控制器应该首先加载模型数据(可能通过存储库接口),然后请求映射器将模型转换为特定于mvvm的ViewModel。

如果你使用依赖注入,那么控制器将接受服务接口作为构造函数参数,而ViewModels将不接受服务作为构造函数参数:它们将是相当愚蠢的数据包。

我要重申一下,这可能有些矫枉过正,但有时也是最好的选择。

谢谢大家的建议。我使用的解决方案如下:

public class SomeClass : ObservableCollection<Elements>
{
  private int _field1;
  public int Field1 
   {
    get
     {
      return _field1;
     }
    set
     {
      _field1 = value;
      OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
     }
   }
  private string _description;
  public string Description 
   {
    get
     {
      return _description;
     }
    set
     {
      _description= value;
      OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
     }
   }
  // remove this since the class is now the collection.
  // public List<MyClass> CollectionOfObjects {get; set;}
}

现在SomeClass扩展了ObservableCollection,因此有了CollectionChangedEvent

所以在我的MVVM类中,我添加了以下内容:

A handler

    private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        this.OnPropertyChanged("ViewableObject");
    }

为事件分配处理程序

private SomeClass _viewableObject;
public SomeClass ViewableObject
{
     get
     {
      return _viewableObject;
     }
     set
     {
      _viewableObject = value;
      if(value != null)
         _viewableObject.CollectionChanged += new NotifyCollectionChangedEventHandler(CollectionChanged)
      OnPropertyChanged("ViewableObject");
     }

} 

考虑到整个问题的开始是,从MVVM的代码会触发的唯一时间是当一个新的赋值发生时,事件处理程序只在需要时调用。我认为这是一个简单的方法。