【问题标题】:How to create ObservableCollection sorted by item's property and emitting CollectionChanged when item's property changes如何创建按项目属性排序的 ObservableCollection 并在项目属性更改时发出 CollectionChanged
【发布时间】:2012-06-21 04:33:53
【问题描述】:

我有一个控件列表,其中每个控件都有 ZIndex 属性:

class WizardControl : INotifyPropertyChanged
{
    public int ZIndex { get; set; /* set emits PropertyChanged event */}
}

class WizardStep
{
    ObservableCollection<WizardControl> Controls { get; set; }
}

class Wizard
{
    ObservableCollection<WizardStep> Steps { get; set; }
}

我还有一个使用HierarchicalDataTemplateTreeView,其中每个WizardStep 都有一个树节点,所有WizardControl 都是树叶。

现在我想按控件的 ZIndex 对控件进行排序。我找到了一个使用自定义 Converter (http://stackoverflow.com/a/5730402/69868) 的解决方案,只要 ZIndex 不变,它就可以正常工作。

当 ZIndex 发生变化时,排序后的 CollectionView 不会发出 CollectionChanged 事件,GUI 不会选择顺序变化。

我的问题:如何创建一个排序后的集合,当项目由于排序值的变化而重新排序时,该集合会发出正确的事件?

【问题讨论】:

    标签: wpf observablecollection collectionview


    【解决方案1】:

    为此,您必须使用 CollectionView 包裹您的 ObservableCollectionCollectionView.SortDescriptions.Add(new SortDescription(ZIndex))

    这样,每当可观察集合中任何项目的ZIndex 发生变化时,它都会自动在 GUI 上获取正确的排序位置。

    【讨论】:

    • 我这样做了,但 GUI 没有接收到 ZIndex 的变化。
    • 很奇怪!! ...这对我有用...您确定您在 setter 中正确引发了 PropertyChanged 事件吗?
    【解决方案2】:

    我想您可以自己在集合中实现 INotifyCollectionChanged 接口,该集合可以监听 WizardControl 的属性更改事件参数,让您完全控制事情的完成方式。我已经包含了一个小示例,说明如何完成。

    WizardControl.cs

        public class WizardControl : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            int zIndex;
            PropertyChangedEventArgs zIndexArgs = new PropertyChangedEventArgs("ZIndex");
    
            public int ZIndex
            {
                get { return zIndex; }
                set
                {
                    if (zIndex != value)
                    {
                        zIndex = value;
                        PropertyChangedEventHandler temp = PropertyChanged;
                        if (temp != null)
                            temp(this, zIndexArgs);
                    }
                }
            }
    
            public override string ToString()
            {
                return zIndex.ToString();      
    
            }
    }
    

    WizardCollection.cs

    public class WizardCollection : INotifyCollectionChanged, IEnumerable<WizardControl>
    {
        public event NotifyCollectionChangedEventHandler CollectionChanged;
        NotifyCollectionChangedEventArgs collectionChangedMoveArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
    
        List<WizardControl> items = new List<WizardControl>();
    
        public WizardControl this[int index]
        {
            get { return items[index]; }
        }
    
        public void Add(WizardControl item)
        {
            if (items == null) items = new List<WizardControl>();
            items.Add(item);
            item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
            NotifyCollectionChangedEventHandler temp = CollectionChanged;
            if (temp != null)
                temp(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
        }
    
        public void Remove(WizardControl item)
        {
            item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
            NotifyCollectionChangedEventHandler temp = CollectionChanged;
            if (temp != null)
                temp(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
        }
    
        void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "ZIndex")
            {
                items = items.OrderBy(x => x.ZIndex).ToList();
                NotifyCollectionChangedEventHandler temp = CollectionChanged;
                if (temp != null)
                    temp(this, collectionChangedMoveArgs);
            }
        }
    
        public IEnumerator<WizardControl> GetEnumerator()
        {
            return items.GetEnumerator();
        }
    
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return items.GetEnumerator();
        }
    }
    

    【讨论】:

      【解决方案3】:

      ObservableCollection 仅在集合更改时引发PropertyChange 通知,而不是在集合内的对象发生更改时。

      如果您想要这种行为,您必须自己添加。通常我将它添加到CollectionChanged 事件中。

      public MyViewModel()
      {
          MyCollection.CollectionChanged += MyCollection_CollectionChanged;
      }
      
      void MyCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
      {
          if (e.NewItems != null)
              foreach(MyItem item in e.NewItems)
                  item.PropertyChanged += MyItem_PropertyChanged;
      
          if (e.OldItems != null)
              foreach(MyItem item in e.OldItems)
                  item.PropertyChanged -= MyItem_PropertyChanged;
      }
      
      void MyItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
      {
          if (e.PropertyName == "Some Property")
          {
              // Do work
              RaisePropertyChanged("MyCollection");
          }
      }
      

      由于您在集合上使用转换器,只需为集合引发 PropertyChanged 事件即可重新运行转换器

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-09-06
        • 2017-11-19
        • 1970-01-01
        • 2011-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-03
        相关资源
        最近更新 更多