当一个字段被改变时,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
的字段。Field1
和Description
被推到一个文本框中,CollectionOfObjects
被推到一个DataGrid
中。
<Datagrid .... ItemsSource = "{Binding ViewableObject.CollectionOfObjects}" ></DataGrid>
所以,如果我只更新ViewableObject.CollectionOfObjects
,而不是整个ViewableObject
,我怎么能让UI更新,以反映修改?是否有一种更清洁的方法来做到这一点,而不是在MVVM中拆分SomeClass
的每个字段?
你可以创建你自己的集合来实现INotifyCollectionChanged,但是你会发现使用ObservableCollection更容易,因为它已经做到了。
这里有几个选项。其中最简单的是将CollectionOfObjects
从List<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对象。它对INotifyPropertyChanged
和ObservableCollection
的依赖使其成为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的代码会触发的唯一时间是当一个新的赋值发生时,事件处理程序只在需要时调用。我认为这是一个简单的方法。