C# 在集合中的项目上更改了可观察集合属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1015126/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 05:35:43  来源:igfitidea点击:

Observable Collection Property Changed on Item in the Collection

c#wpfsortingcollectionsobservablecollection

提问by Nate

I have an ObservableCollection<T>. I've bound it to a ListBox control and I've added SortDescriptionsto the Items collection on the ListBox to make the list sort how I want.

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

I want to resort the list at ANYpoint when any property changed on a child element.

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

All my child elements implement INotifyPropertyChanged.

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

采纳答案by micahtan

Brute force:

蛮力:

  1. Attach handler to each PropertyChanged event for each child item
  2. Grab the ListCollectionView from your CollectionViewSource
  3. Call Refresh.
  1. 将处理程序附加到每个子项的每个 PropertyChanged 事件
  2. 从 CollectionViewSource 中获取 ListCollectionView
  3. 呼叫刷新。

EDIT:

编辑:

The code for 1, 2 would live in your code-behind.

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

For #1, you'd do something like:

对于#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**
....
      }
}

For #2, in your CollectionChanged handler, you would do something like:

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

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

EDIT2: However, in this case, I would stronglysuggest that you also check ListCollectionView.NeedsRefresh and only refresh if that is set. There's no reason to re-sort if your properties have changed which don't affect the sort.

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

回答by apandit

This works. Whenever the collection changes, it re-sorts the collection. Might be doable in a more efficient way but this is the gist of it.

这有效。每当集合更改时,它都会重新排序集合。可能以更有效的方式可行,但这是它的要点。


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:

XAML:

<Window x:Class="ServiceManager.TestWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="TestWindow" Height="500" Width="500">
    <DockPanel>
        <ListBox ItemsSource="{Binding}" x:Name="lstbox">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Label Content="{Binding Path=A}"/>
                        <Label Content="{Binding Path=B}"/>
                        <Label Content="{Binding Path=C}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button Click="Button_Click" Content="Click" />
    </DockPanel>
</Window>