【问题标题】:Observable Collection Property Changed on Item in the Collection集合中项目的可观察集合属性已更改
【发布时间】:2010-11-04 03:32:30
【问题描述】:

我有一个ObservableCollection<T>。我已将它绑定到 ListBox 控件,并将SortDescriptions 添加到 ListBox 上的 Items 集合中,以使列表按我想要的方式排序。

当子元素的任何属性发生更改时,我想在 ANY 点使用列表。

我所有的子元素都实现了INotifyPropertyChanged

【问题讨论】:

  • 那么,你将你的 OC 绑定到一个 Listbox 并且在 listbox 上有排序描述?
  • 没错。当子项的属性发生更改时,我希望排序能够反映此更改。

标签: c# wpf sorting collections observablecollection


【解决方案1】:

蛮力:

  1. 将处理程序附加到每个子项的每个 PropertyChanged 事件
  2. 从您的 CollectionViewSource 中获取 ListCollectionView
  3. 调用刷新。

编辑:

1、2 的代码将存在于您的代码隐藏中。

对于 #1,您可以执行以下操作:

private void Source_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    switch (e.Action)
    {
        case NotifyCollectionChangedAction.Add:
            foreach( SomeItem item in e.NewItems)
            {
               item.PropertyChanged += new PropertyChangedEventHandler(_SomeItem_PropertyChanged); 
            }
            break;
....
**HANDLE OTHER CASES HERE**
....
      }
}

对于 #2,在您的 CollectionChanged 处理程序中,您可以执行以下操作:

private void _SomeItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    ListCollectionView lcv = (ListCollectionView)(CollectionViewSource.GetDefaultView(theListBox.ItemsSource));
    lcv.Refresh();
}

编辑2: 但是,在这种情况下,我会强烈建议您也检查 ListCollectionView.NeedsRefresh 并仅在设置时刷新。如果您的属性已更改且不影响排序,则没有理由重新排序。

【讨论】:

  • 此代码是否会存在于我的表示层中? Window.Xaml.Cs? #1 和 #2 的代码是什么样的?
  • 这正是我所需要的。我最终只使用了第二部分,因为在我的情况下,我有一个导致更改的事件,所以我只需要 #2。
【解决方案2】:

这行得通。每当集合发生变化时,它都会对集合进行重新排序。可能以更有效的方式可行,但这是它的要点。

public partial class TestWindow : Window { ObservableCollection<TestClass> oc; public TestWindow() { InitializeComponent(); // Fill in the OC for testing oc = new ObservableCollection<TestClass>(); foreach( char c in "abcdefghieeddjko" ) { oc.Add( new TestClass( c.ToString(), c.ToString(), c.GetHashCode() ) ); } lstbox.ItemsSource = oc; // Set up the sorting (this is how you did it.. doesn't work) lstbox.Items.SortDescriptions.Add( new SortDescription("A", ListSortDirection.Ascending) ); // This is how we're going to do it oc.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler( oc_Sort ); } void oc_Sort( object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e ) { // This sorts the oc and returns IEnumerable var items = oc.OrderBy<TestClass, int>( ( x ) => ( x.C ) ); // Rest converst IEnumerable back to OC and assigns it ObservableCollection<TestClass> temp = new ObservableCollection<TestClass>(); foreach( var item in items ) { temp.Add( item ); } oc = temp; } private void Button_Click( object sender, RoutedEventArgs e ) { string a = "grrrr"; string b = "ddddd"; int c = 383857; oc.Add( new TestClass( a, b, c ) ); } } public class TestClass : INotifyPropertyChanged { private string a; private string b; private int c; public TestClass( string f, string g, int i ) { a = f; b = g; c = i; } public string A { get { return a; } set { a = value; OnPropertyChanged( "A" ); } } public string B { get { return b; } set { b = value; OnPropertyChanged( "B" ); } } public int C { get { return c; } set { c = value; OnPropertyChanged( "C" ); } } #region onpropertychanged public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged( string propertyName ) { if( this.PropertyChanged != null ) { PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) ); } } #endregion }

XAML:

堆栈面板> 数据模板> 列表框> 窗口>

【讨论】: