views:

1224

answers:

1

I have a data set whose elements are displayed as rows in a DataGrid. The sort order for the rows changes in response to external events.

My initial thought was to store the rows as an ObservableCollection and resort the collection after updates. However I ran into two problems: 1) the ObservableCollection does not have a Sort() method 2) if I try to sort the elements myself, I get an exception whenever I try to assign an element to a new position, for example in a swap function like

class MyCollection : ObservableCollection<T>
{
   void swap( int i, int j )
   {
      T tmp = this[i];
      this[i] = this[j]; // THROWS A NOT SUPPORTED EXCEPTION
      this[j] = tmp;
   }
}

So the question is ... how to populate a DataGrid whose row order needs to update dynamically?

I did finally get one answer working, I'll describe it below.

+1  A: 

I got this to work by implementing INotifyCollectionChanged explicitly (instead of using ObservableCollection). Furthermore, I found that using the Update action resulted in the same "not supported" error, but that I could use the Add and Remove actions. So my swap function ends up looking like this:

class MyCollection<T> : List<T>, INotifyCollectionChanged
{
   public event NotifyCollectionChangedEventHandler CollectionChanged;

   private void swap( int i, int j )
   {
      T a = this[i];
      T b = this[j];

      // swap my own internal data storage
      this[i] = b;
      this[j] = a;

      // and also let my CollectionChanged listener know that I have done so.
      if( CollectionChanged != null )
      {
         NotifyCollectionChangedEventArgs arg;

         arg = new NotifyCollectionChangedEventArgs(
             NotifyCollectionChangedAction.Remove, a, i );
         CollectionChanged( this, arg );

         arg = new NotifyCollectionChangedEventArgs(
             NotifyCollectionChangedAction.Add, b, i );
         CollectionChanged( this, arg );

         arg = new NotifyCollectionChangedEventArgs(
             NotifyCollectionChangedAction.Remove, b, j );
         CollectionChanged( this, arg );

         arg = new NotifyCollectionChangedEventArgs(
             NotifyCollectionChangedAction.Add, a, j );
         CollectionChanged( this, arg );

      }

   }

}

The dynamic changes are fairly local, so fortunately using a slower handwritten sort in response to changes is working OK for me. In other words, when updates arrive, I invoke another member function (in the same collection) that looks something like this:

public void ProcessUpdates( List<T> updateList )
{
    // use the contents of updateList to modify my internal store
    // ...


    // and now resort myself
    sort();
}

private void sort()
{
    // implement your favorite stable sorting algorithm here, calling 
    // swap() whenever you swap two elements.

    // (this is an intentionally facetious sorting algorithm, because I
    // don't want to get into the long and irrelevant details of my own 
    // data storage.)
    while( i_am_not_sorted() )
    {
       int i = random_index();
       int j = random_index();
       if( out_of_order(i,j) )
       {
          // modify my internal data structure and 
          // also let my CollectionChanged listener know that I have done so
          swap( i, j );
       }
    }
}

Don't forget that it's also necessary to fire an "Add" notification when adding elements to the collection! I sort the initial list and then add in sorted order, which lets me use a more efficient library sort when I first populate the data.

Eric