C# INotifyPropertyChanged不适用于ObservableCollection中持有的类<;类别>;
我有一个类“BaseClass”,它实现了C# INotifyPropertyChanged不适用于ObservableCollection中持有的类<;类别>;,c#,mvvm,uwp,inotifypropertychanged,C#,Mvvm,Uwp,Inotifypropertychanged,我有一个类“BaseClass”,它实现了INotifyPropertyChanged,并具有以下功能: 基类: private bool isOn; public bool IsOn { get { return isOn; } set { isOn = value; RaisePropertyChanged("BaseClass:IsOn");
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
。它还有一个可观察的集合
:
二类:
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));
}
}
为什么INPC接口不能使用嵌套属性?这个问题和答案似乎很相关,但我想不出来
编辑:附加说明和代码:
我可以注册到可观察集合
项目的属性更改
事件,例如:
ClassesOfA[0].PropertyChanged += DIClass_PropertyChanged;
ClassesOfA[1].PropertyChanged += DIClass_PropertyChanged;
但是,这仍然不会冒泡通知myViewModel
,myDIClass
的ObservableCollection
的属性已更改。我想使用INPC通过MVVM层弹出事件信息/属性更新。但我想“包装”它们,使我的类更干净/更少的属性
编辑:
我添加了我的问题/场景的“草图”,并使用了基本命名,以使其更简单:
回答你的问题:这是设计的 ObservableCollection有两个事件:
- CollectionChanged:当集合更改时激发,例如,
collection.Add(item)
- PropertyChanged:当属性更改时激发,例如
collection=new ObservableCollection()代码>
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();
}
公共列表集合{get;set;}
public void InitializeCollection(IEnumerable baseClassCollection){
集合=新列表();
foreach(baseClassCollection中的var项){
item.PropertyChanged+=允许属性更改的方法;
集合。添加(项目);
}
}
public void方法允许属性更改(对象发送方,PropertyChangedEventArgs e){
//对任何属性更改作出反应
doSomething();
//对特定属性作出反应
如果(e!=null&&e.PropertyName.Equals(“isOn”))
doSomethingOtherStuff();
}
这可能非常烦人,并可能导致其他一些问题。
如果我遇到这种情况,我会考虑重新设计ViewModels和UI。我会尝试拥有一个绑定到每个基类项的UI。例如,如果我有一个ListView,我会提供一个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, sender, IndexOf((T)sender));
OnCollectionChanged(args);
}
}
公共类双类:INotifyPropertyChanged
{
公共外部可观测集合类ESOFA
…其他代码。。。
}
公共密封类ExoObservableCollection:ObservableCollection
其中T:INotifyPropertyChanged
{
公共ExObservableCollection()
{
CollectionChanged+=AllObservableCollectionCollectionChanged;
}
公共外部可观测集合(IEnumerable pItems):this()
{
foreach(pItems中的var项目)
{
本条增加(项目);
}
}
私有void AllObservableCollectionCollectionChanged(对象发送方,NotifyCollectionChangedEventArgs e)
{
如果(如NewItems!=null)
{
foreach(e.NewItems中的对象项)
{
((INotifyPropertyChanged)项)。PropertyChanged+=项PropertyChanged;
}
}
如果(例如,OldItems!=null)
{
foreach(e.OldItems中的对象项)
{
((INotifyPropertyChanged)项)。PropertyChanged-=项PropertyChanged;
}
}
}
私有void ItemPropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs args=新的NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace,sender,sender,IndexOf((T)sender));
OnCollectionChanged(args);
}
}
然后可以在DIClass
对象中使用exobbservablecollection
类。当基类
中的属性更改时,UI将被更新
更新:
最后,我根据复杂的示例发现了您提到的意外行为。exobbservablecollection
类运行良好,并正确激发属性更改事件
关键的一点是,您认为如果baseclass
中的属性更改事件被触发,那么它将被触发
在DIClass
中也触发属性更改事件,对吗?我不得不说那是不对的。属性更改事件仅在当前类中激发。除非在父类中处理它,否则它不会传递给父类。它只触发一次,并在目标属性更改时通知UI
如果我正确理解了您的场景,那么当BaseClass
对象中的相同属性发生更改时,您希望更改ToggleButton的状态。但是切换按钮绑定到VMData
对象,因此当DIClass
对象中的BaseClass
对象发生更改时,您需要得到通知。因此,您希望BaseCasss
的属性更改事件触发DIClass
的属性更改事件
在DIClass
对象中处理BaseClass
的属性更改事件是执行所需操作的正确方法。这与在ViewModel中处理DIClass
事件相同。但是您不想要它,因为可能有很多对象
那么您的样本的第一个版本就是rec
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, sender, IndexOf((T)sender));
OnCollectionChanged(args);
}
}