views:

3462

answers:

2

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

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

All my child elements implement INotifyPropertyChanged.

+4  A: 

Brute force:

  1. Attach handler to each PropertyChanged event for each child item
  2. Grab the ListCollectionView from your CollectionViewSource
  3. 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.

micahtan
Would this code live in my presentation tier? Window.Xaml.Cs?What would code for #1 and #2 look like?
Nate Bross
That is exactly what I needed. I ended up only using the second part, since in my case I have an event that is causing the change, so I only needed #2.
Nate Bross
+1  A: 

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>
apandit