Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF - Using a custom comparer when sorting by multiple columns

I have a ListView (GridView) that I want to sort by 2 columns, so if 2+ items have the same value in Column 1, it sorts by Column 2. Pretty easy. But empty strings show up at the top when sorting A-Z. I'd like to move them to the bottom. I made a comparer (IComparer) that takes care of this, but I'm not sure how to use it.

Here's the code I tried:

 Dim view As ListCollectionView = CollectionViewSource.GetDefaultView(myCollection)
 Using view.DeferRefresh
    view.SortDescriptions.Clear()
    view.SortDescriptions.Add(New SortDescription(sortHeader.Header, direction))
    view.SortDescriptions.Add(New SortDescription(otherColumn, direction))
    view.CustomSort = New MyComparer()
 End Using

The problem is my comparer is given a type of my class instead of the value of the property/column being sorted by. So if the class is Foo and I'm sorting by Foo.Bar, I get the entire Foo class, not just the value of Bar (which is really all it should be concerned about, since that's what it's sorting by).

How will my comparer know which property to compare? Maybe I'm doing something wrong here, because this doesn't make any sense. I expected to get a String (the property type) for x and y...

Does anyone know how to do this?

like image 679
grant Avatar asked Apr 07 '11 22:04

grant


1 Answers

Your IComparer implementation will be given the entire object, you need to figure out which column gets clicked, probably by doing something like this:

this.AddHandler(GridViewColumnHeader.ClickEvent, 
                new RoutedEventHandler(Column_Sort));

and then feed that into your MyComparer probably by modifying your constructor to take in the property path.

In Column_Sort you can get the property path something like this (I'm a little rusty on vb, but c# would do this:

void Column_Sort(object sender, RoutedEventArgs e)
{
  var memberBinding= ((GridViewColumnHeader)e.OriginalSource).Column.DisplayMemberBinding;
  var path = ((Binding)memberBinding).Path.Path;
}

and then feed that into your sorting logic.

Dim view As ListCollectionView = CollectionViewSource.GetDefaultView(myCollection)
 Using view.DeferRefresh
    view.SortDescriptions.Clear()
    view.SortDescriptions.Add(New SortDescription(sortHeader.Header, direction))
    view.SortDescriptions.Add(New SortDescription(otherColumn, direction))
    view.CustomSort = New MyComparer(PropertyPath)
 End Using

EDIT: You just need to customize your IComparer to support multiple column sorting, I googled and found this comparer implementation that you can feed it several property paths separated by commas and suffixed with ASC/DESC like this:

User.LastName DESC, User.FirstName DESC

I think the convention for multi-column sorting as far as UI is concerned is that you need to have the Ctrl button held. So subclass the ListView class and tie into the GridViewColumnHeader clicked event and the KeyDown event to know which columns were clicked in succession and then use an IComparer implementation to feed that into.

like image 83
Jose Avatar answered Oct 04 '22 02:10

Jose