【问题标题】:binding to an Observable Collection in a usercontrol and getting notifications from it绑定到用户控件中的可观察集合并从中获取通知
【发布时间】:2015-01-14 17:26:12
【问题描述】:

我在这里查看了一些与我的问题相似的其他问题,但没有一个答案。所以,就这样吧……

我正在使用 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>
{
}

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 这些变化并相应地更新。但此刻,没有喜悦。数据在那里,因为如果手动强制刷新 ChartsControl,则会显示更改。我已经尝试绑定到 MainGroups 属性,希望这会起作用,但那也不起作用。

我可能错过了一些非常明显的东西,但我不确定是什么。有什么想法吗?

罗伯

【问题讨论】:

    标签: c# wpf mvvm binding observablecollection


    【解决方案1】:

    ObservableCollection 仅侦听集合中发生的更改,例如添加或删除的项目,它不会通知其集合的各个项目中发生的任何更改。

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

    最后调用 ClearItems 会释放所有事件处理程序。

    更多详情可以从下面的链接中找到。

    How to Listen to Property Changes of Items of an 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));
            }
        }
    }
    

    【讨论】:

    • @gunr2171 - 根据您的建议进行编辑。
    • 这是一个非常有用的类,我可能会继续使用它,但不幸的是它并不能解决我的问题。即使正确通知了任何更改,通过用户控件绑定集合也不会导致发生动态更新。这就是为什么我想知道是什么导致了这个问题。
    • @RobMarsh - 我可以免费获得所需的功能,而无需使用 ItemsControl、ListBox、TreeView 来处理上述问题。我想知道您使用什么控件来绑定 ObservableCollection。
    • 我实际上正在绑定到另一个用户控件,所以这可能是问题所在...我必须尝试使用​​标准控件,看看会发生什么。
    【解决方案2】:

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

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

    1. OnChartGroupsChanged 注册该事件并自行处理(确保取消注册旧的,以免泄露句柄!)

    2. 派生自 ItemsControl 之类的东西并使用其 Items 属性。这将删除您的依赖属性,并允许 ItemsControl 为您处理所有集合更改的内容。

    如果可行,我会选择第二个。如果没有,利用CollectionChanged 事件应该不会不好。

    【讨论】:

      猜你喜欢
      • 2017-09-28
      • 2016-12-19
      • 2010-11-18
      • 2011-11-16
      • 1970-01-01
      • 2020-06-05
      • 2011-04-25
      • 2020-12-29
      • 1970-01-01
      相关资源
      最近更新 更多