Observable Collection Property Changed on Item in the Collection
Brute force:
- Attach handler to each PropertyChanged event for each child item
- Grab the ListCollectionView from your CollectionViewSource
- Call Refresh.
EDIT:
The code for 1, 2 would live in your code-behind.
For #1, you'd do something like:
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:
private void _SomeItem_PropertyChanged(object sender, PropertyChangedEventArgs e){ ListCollectionView lcv = (ListCollectionView)(CollectionViewSource.GetDefaultView(theListBox.ItemsSource)); lcv.Refresh();}
EDIT2:However, in this case, I would strongly suggest 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.
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:
<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>