【问题标题】:ItemsSource and collections in which only element properties change仅元素属性更改的 ItemsSource 和集合
【发布时间】:2011-10-10 13:04:58
【问题描述】:

我对 ComboBox 不反映其 ItemsSource 所绑定的集合的属性的变化感到悲痛。

有一棵树由包含设置对象的类别对象的类别可观察集合组成。某些设置定义了其他设置允许的值域的表示名称。这些分布在多个类别中,但使用 LINQ 的小技巧会产生一个 ObservableCollection,其中 ChannelCategory 公开属性 ChannelNumber 和 ChannelTitle。

ChannelCategory.ChannelTitle 是 StringSetting,ChannelNumber 是 int,ChannelTitle 为其提供显示字符串的不可变标识值。省略大量但不相关的其他节点,我们有以下内容:

Channels
  ChannelCategory
    ChannelNumber = 1
    ChannelTitle = "caption for channel one"
  ChannelCategory
    ChannelNumber = 2
    ChannelTitle = "caption for channel two"
  ChannelCategory
    ChannelNumber = 3
    ChannelTitle = "caption for channel three"
...

这个 Channels 集合由一个类的属性准备和公开,该类实例化并添加到 XAML 中的窗口资源字典(可作为 StaticResource 访问)。这种安排允许我以声明方式将组合框绑定到 Channels

    <ComboBox VerticalAlignment="Center" Grid.Column="2" 
              ItemsSource="{Binding Source={StaticResource cats}, Path=Channels}"
              DisplayMemberPath="ChannelTitle.Editor"
              SelectedValuePath="ChannelNumber"
              SelectedValue="{Binding Editor}"
              />

这一切都有效,但其他地方对 ChannelTitle 值的编辑不会反映在组合框列表中显示的值中。

各种带有断点的调试技巧和 DropDownOpened 事件使我能够确定更新可从 ItemsSource 引用的集合中获得

最后我们找到了谜团。为什么组合框没有检测到集合元素的更改?集合本身是一个 ObservableCollection,因此它应该通知属性更改。

集合的元素都是ChannelCategory,是一个DependencyObject,ChannelCategory.ChannelTitle是一个DependencyProperty。

认为问题是我既没有在集合中添加也没有删除项目,所以就集合而言,它具有相同的元素,因此没有改变。

任何人都可以建议一种策略来更改 ChannelTitle 以导致 Channels 集合通知更改以便组合框更新吗?


Rachel 的建议如下所示。在我的应用程序的上下文中,复杂性要高得多,因为每个 ChannelCategory 都拥有一组设置对象,因此更改的值是集合中对象的属性,它是 ItemsSource 绑定到的集合中对象的属性.但雷切尔建议的精髓只需要在两个层面上应用即可。

public class ObservableChannelCollection : ObservableCollection<ChannelCategory>
{
  protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
  {
    if (e.NewItems != null)
      foreach (ChannelCategory channel in e.NewItems)
        channel.PropertyChanged += channel_PropertyChanged;
    if (e.OldItems != null)
      foreach (ChannelCategory channel in e.OldItems)
        channel.PropertyChanged -= channel_PropertyChanged;
    base.OnCollectionChanged(e);
  }
  void channel_PropertyChanged(object sender, PropertyChangedEventArgs e)
  {
    OnPropertyChanged(e);
  }
}

【问题讨论】:

    标签: wpf binding itemssource


    【解决方案1】:

    ObservableCollection 跟踪集合本身的更改,例如AddRemoveReset 等,但它不跟踪集合中单个项目的更改。因此,如果您更新 ObservableCollection 中某个项目的属性,则集合不会收到更改的通知。

    另一种方法是向ObservableCollection.CollectionChanged 事件添加一个事件,当添加新项目时,在新项目上关联一个属性更改,这将引发集合更改事件。

    void MyObservableCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach(MyItem item in e.NewItems)
            {
                MyItem.PropertyChanged += MyItem_PropertyChanged;
            }
        }
    
        if (e.OldItems!= null)
        {
            foreach(MyItem item in e.OldItems)
            {
                MyItem.PropertyChanged -= MyItem_PropertyChanged;
            }
        }
    }
    
    void MyItem_PropertyChanged(object sender, PropertyChange e)
    {
        RaisePropertyChanged("MyObservableCollection");
    }
    

    【讨论】:

    • 这几乎就是我的想法;我无法形成这个想法,我很感激。我从 ObservableCollection 派生了一个类,并为它提供了实现您的建议的 ctor 和事件处理程序方法,以及扩展 ChannelCategories 以显示所需的 PropertyChanged 事件。我们的策略能否奏效还有待观察;我会及时通知你。
    • 效果很好。谢谢。我想你对让 XamlServices 正确序列化枚举没有任何想法?
    猜你喜欢
    • 1970-01-01
    • 2019-03-13
    • 1970-01-01
    • 1970-01-01
    • 2016-03-28
    • 2012-01-01
    • 2012-04-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多