问题描述
我有一个类,“BaseClass”,它实现了 INotifyPropertyChanged
并具有以下内容:
基类:
private bool isOn;
public bool IsOn
{
get { return isOn; }
set
{
isOn = value;
RaisePropertyChanged("BaseClass:IsOn");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(name));
}
}
然后我有一个类,“DIClass”,它也实现了 INotifyPropertyChanged
。它还有一个 ObservableCollection<BaseClass>
:
DIClass:
public ObservableCollection<BaseClass> ClassesOfA;
private string iNPCTest;
public string INPCTest
{
get { return iNPCTest; }
set
{
iNPCTest = value;
RaisePropertyChanged("DIClass: INPCTest");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(name));
}
}
我的 viewmodel 拥有一个“DIClass”实例并注册到它的 PropertyChanged
事件。当我在“DIClass”中设置 INPCTest
的值时,viewmodel 会正确“捕获”事件。但是,当我更新 IsOn
中的 ObservableCollection
属性时,如下所示,viewmodel 中没有选择该事件。
ClassesOfA[0].IsOn = true;
为什么 INPC 接口不适用于嵌套属性?问题和答案 here 似乎很相关,但我想不通。
编辑:附加说明和代码:
我可以注册 PropetyChanged
项的 ObservableCollection
事件,例如:
ClassesOfA[0].PropertyChanged += DIClass_PropertyChanged;
ClassesOfA[1].PropertyChanged += DIClass_PropertyChanged;
但是,这仍然没有冒泡通知我的 viewmodel
,我的 DIClass
的 ObservableCollection<BaseClass>
的属性已更改。我想使用 INPC 通过 MVVM 层冒泡事件信息/属性更新。但我想“包装”它们以使我的类更清洁/更少的属性
编辑:
我添加了我的问题/场景的“草图”,并使用了基本命名以使其变得简单:
解决方法
回答您的问题:这是设计使然。
ObservableCollection 有两个事件:
- CollectionChanged:当集合改变时触发,例如
collection.Add( item )
- PropertyChanged:当属性改变时触发,例如
collection = new ObservablecCollection<T>();
我认为您不需要 ObservableCollection,因为-据我了解您的问题-您想观察集合中项目属性的变化。要实现这一点,您需要像这样注册到每个观察到的项目的 PropertyChanged:
public List<BaseClass> Collection {get;set;}
public void InitializeCollection( IEnumerable<BaseClass> baseClassCollection){
Collection = new List<BaseClass>();
foreach(var item in baseClassCollection){
item.PropertyChanged += MethodToCallOnPropertyChanges;
Collection.Add( item );
}
}
public void MethodToCallOnPropertyChanges(object sender,PropertyChangedEventArgs e){
//react to any property changes
doSomething();
//react to specific properties
if(e != null && e.PropertyName.Equals("isOn"))
doSomethingOtherStuff();
}
这可能非常烦人,并可能导致一些其他问题。 如果遇到这种情况,我会考虑重新设计 ViewModel 和 UI。我会尝试拥有一个绑定到每个 BaseClass 项目的 UI。例如,如果我有一个 ListView,我将提供一个绑定 BaseClass 项目的 ItemTemplate。这样做将防止需要注册到每个项目的 PropertyChanged。
,我的建议是,您可以创建一个自定义的 ObservableCollection 类,当列表项上的属性发生更改时,该类会引发重置操作。它强制所有项目实现 INotifyPropertyChanged。
我做了一个简单的演示,你可以查看:
public class DIClass : INotifyPropertyChanged
{
public ExObservableCollection<BaseClass> ClassesOfA
... other code...
}
public sealed class ExObservableCollection<T> : ObservableCollection<T>
where T : INotifyPropertyChanged
{
public ExObservableCollection()
{
CollectionChanged += AllObservableCollectionCollectionChanged;
}
public ExObservableCollection(IEnumerable<T> pItems) : this()
{
foreach (var item in pItems)
{
this.Add(item);
}
}
private void AllObservableCollectionCollectionChanged(object sender,NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Object item in e.NewItems)
{
((INotifyPropertyChanged)item).PropertyChanged += ItemPropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (Object item in e.OldItems)
{
((INotifyPropertyChanged)item).PropertyChanged -= ItemPropertyChanged;
}
}
}
private void ItemPropertyChanged(object sender,PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,sender,IndexOf((T)sender));
OnCollectionChanged(args);
}
}
然后您可以在 ExObservableCollection
对象中使用 DIClass
类。当 BaseClass
中的属性发生变化时,UI 将更新。
更新:
最后,我根据复杂的样本发现了您提到的意外行为。 ExObservableCollection
类运行良好并正确触发属性更改事件。
关键是你认为如果 baseclass
中的属性更改事件被触发,那么它会
也触发 DIClass
中的属性更改事件,对吗?我不得不说这是不正确的。属性更改事件仅在当前类中触发。除非您在父类中处理它,否则它不会传递给父类。它只触发一次并在目标属性更改时通知 UI。
如果我正确理解您的情况,您希望在更改 BaseClass
对象中的相同属性时更改 ToggleButton 的状态。但是 ToggleButton 绑定到 VMData
对象,因此您需要在 BaseClass
对象中的 DIClass
对象发生更改时收到通知。所以你想让BaseCasss
的属性改变事件触发DIClass
的属性改变事件。
处理 BaseClass
对象中 DIClass
的属性更改事件是实现您想要的正确方法。这与在 ViewModel 中处理 DIClass
事件相同。但是你不想要它,因为可能有很多对象。
然后,您的示例的第一个版本是通过自己触发 DIClass
的属性更改事件来实现您想要的目标的推荐方法。