C# 绑定到usercontrol中的可观察集合并从中获取通知

C# 绑定到usercontrol中的可观察集合并从中获取通知,c#,wpf,mvvm,binding,observablecollection,C#,Wpf,Mvvm,Binding,Observablecollection,我一直在看一些与我的问题类似的其他问题,但没有一个答案。所以,这是 我正在使用WPF和MVVM模式开发一个C#应用程序。我有一个UserControl(称为GroupTree),它包含一个依赖项属性: public static DependencyProperty ChartGroupsProperty = DependencyProperty.Register("ChartGroups", typeof(ChartGroupCollection), typeof(GroupTree),

我一直在看一些与我的问题类似的其他问题,但没有一个答案。所以,这是

我正在使用WPF和MVVM模式开发一个C#应用程序。我有一个UserControl(称为GroupTree),它包含一个依赖项属性:

  public static DependencyProperty ChartGroupsProperty = DependencyProperty.Register("ChartGroups", typeof(ChartGroupCollection), typeof(GroupTree),
                                                      new FrameworkPropertyMetadata() { DefaultValue=new ChartGroupCollection(),  PropertyChangedCallback = OnChartGroupsChanged, BindsTwoWayByDefault = true });
    public ChartGroupCollection ChartGroups
    {
        get { return (ChartGroupCollection)GetValue(ChartGroupsProperty); }
        set
        {
            SetValue(ChartGroupsProperty, value);
        }
    }

    private static void OnChartGroupsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (e.OldValue != e.NewValue)
        {
            // Nothing at the moment
        }
    }
其类型为ChartGroupCollection,其定义如下:

public class ChartGroupCollection : ObservableCollection<ChartGroup>
{
}
公共类ChartGroupCollection:ObservableCollection
{
}
ChartGroup包含使用INotifyProperty的属性,我已经独立验证了应该为主集合触发的所有更改事件是否都正确执行了该操作

使用控件的MainWindow.Xaml:

   <Controls:GroupTree x:Name="Groups" ChartGroups="{Binding MainGroups}" />

我有另一个控件绑定到GroupTree控件中的ChartGroups值:

   <Controls:ChartsControl x:Name="Charts1_1" Tree="{Binding ElementName=Groups, Path=ChartGroups}" />

我希望发生的是当GroupTree ChartGroups值更改时,动态地通知ChartsControl这些更改并相应地更新。但此刻,没有欢乐。数据就在那里,因为如果手动强制刷新图表控件,则会显示更改。我曾尝试绑定到MainGroups属性,希望它能起作用,但也不行

我可能错过了一些显而易见的东西,但我不确定是什么。有什么想法吗


Rob

不幸的是,
PropertyChanged
回调仅在针对绑定属性引发
PropertyChanged
时调用(从而导致绑定evalutate)

您需要
INotifyCollectionChanged
提供的
CollectionChanged
事件。所以你有两个选择:

  • OnChartGroupsChanged
    中注册该事件,并自己处理它(确保取消旧事件的注册,以免泄露句柄!)

  • 从类似
    ItemsControl
    的内容派生并使用其
    Items
    属性。这将删除依赖项属性,并允许
    ItemsControl
    为您处理所有已更改的集合内容


  • 如果可行的话,我会选择第二个。如果没有,那么利用
    CollectionChanged
    事件应该不会太糟糕。

    不幸的是,只有在针对绑定属性引发
    PropertyChanged
    时才会调用
    PropertyChanged
    回调(从而导致绑定求值)

    您需要
    INotifyCollectionChanged
    提供的
    CollectionChanged
    事件。所以你有两个选择:

  • OnChartGroupsChanged
    中注册该事件,并自己处理它(确保取消旧事件的注册,以免泄露句柄!)

  • 从类似
    ItemsControl
    的内容派生并使用其
    Items
    属性。这将删除依赖项属性,并允许
    ItemsControl
    为您处理所有已更改的集合内容


  • 如果可行的话,我会选择第二个。如果没有,则使用
    CollectionChanged
    事件应该不会太糟糕。

    ObservableCollection只侦听集合中发生的更改,如添加或删除的项,而不会通知其集合中的单个项中发生的任何更改

    下面的类通过将自身注册到要添加的项的INofityPropertyChanged事件来增强ObservaleCollection的功能,并在集合项的属性更改时引发ObservaleCollection的CollectionChanged事件

    在末尾调用ClearItems将释放所有事件处理程序

    更多详细信息可从以下链接中找到

    使用System.ComponentModel;
    使用System.Collections.ObjectModel;
    使用System.Collections.Specialized;
    使用系统集合;
    命名空间VJCollections
    {
    /// 
    ///该类添加了在任何属性出现时刷新列表的功能
    ///对象在实现INotifyPropertyChanged的列表中更改。
    /// 
    /// 
    公共类项目ChangeObservableCollection:
    可观察到的集合,其中T:InotifyProperty已更改
    {
    CollectionChanged上的受保护覆盖无效(NotifyCollectionChangedEventArgs e)
    {
    if(e.Action==NotifyCollectionChangedAction.Add)
    {
    登记财产变更(如新项目);
    }
    else if(e.Action==NotifyCollectionChangedAction.Remove)
    {
    未注册的财产变更(如旧物品);
    }
    else if(e.Action==NotifyCollectionChangedAction.Replace)
    {
    未注册的财产变更(如旧物品);
    登记财产变更(如新项目);
    }
    基础。变更的集合(e);
    }
    受保护的覆盖无效ClearItems()
    {
    未注册的财产变更(本);
    base.ClearItems();
    }
    私有无效注册表属性更改(IList项)
    {
    foreach(项目中的INotifyPropertyChanged项目)
    {
    如果(项!=null)
    {
    item.PropertyChanged+=新的PropertyChangedEventHandler(item\u PropertyChanged);
    }
    }
    }
    私有无效未注册属性已更改(IList项)
    {
    foreach(项目中的INotifyPropertyChanged项目)
    {
    如果(项!=null)
    {
    item.PropertyChanged-=新的PropertyChangedEventHandler(item\u PropertyChanged);
    }
    }
    }
    私有无效项\u PropertyChanged(对象发送方,PropertyChangedEventArgs e)
    {
    base.OnCollectionChanged(新的NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
    }
    }
    
    ObservableCollection只侦听集合中发生的更改,如添加或删除的项
    using System.ComponentModel;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.Collections;
    
    namespace VJCollections
    {
        /// <summary>
        ///     This class adds the ability to refresh the list when any property of
        ///     the objects changes in the list which implements the INotifyPropertyChanged. 
        /// </summary>
        /// <typeparam name="T">
        public class ItemsChangeObservableCollection<T> :
               ObservableCollection<T> where T : INotifyPropertyChanged
        {
            protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
            {
                if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    RegisterPropertyChanged(e.NewItems);
                }
                else if (e.Action == NotifyCollectionChangedAction.Remove)
                {
                    UnRegisterPropertyChanged(e.OldItems);
                }
                else if (e.Action == NotifyCollectionChangedAction.Replace)
                {
                    UnRegisterPropertyChanged(e.OldItems);
                    RegisterPropertyChanged(e.NewItems);
                }
    
                base.OnCollectionChanged(e);
            }
    
            protected override void ClearItems()
            {
                UnRegisterPropertyChanged(this);
                base.ClearItems();
            }
    
            private void RegisterPropertyChanged(IList items)
            {
                foreach (INotifyPropertyChanged item in items)
                {
                    if (item != null)
                    {
                        item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
                    }
                }
            }
    
            private void UnRegisterPropertyChanged(IList items)
            {
                foreach (INotifyPropertyChanged item in items)
                {
                    if (item != null)
                    {
                        item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
                    }
                }
            }
    
            private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            }
        }
    }