C# INotifyPropertyChanged不适用于ObservableCollection中持有的类<;类别>;

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");

我有一个类“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
。它还有一个
可观察的集合

二类:

    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;
但是,这仍然不会冒泡通知my
ViewModel
,my
DIClass
ObservableCollection
的属性已更改。我想使用INPC通过MVVM层弹出事件信息/属性更新。但我想“包装”它们,使我的类更干净/更少的属性

编辑:

我添加了我的问题/场景的“草图”,并使用了基本命名,以使其更简单:


回答你的问题:这是设计的

ObservableCollection有两个事件:

  • CollectionChanged:当集合更改时激发,例如,
    collection.Add(item)
  • PropertyChanged:当属性更改时激发,例如
    collection=new ObservableCollection()
我认为您不需要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);
    }
}