Search code examples
c#wpftableviewcomparator

Custom comparer for the SortDescriptions in the ICollectionView


I have the next problem. There is some WPF dialog with DataGrid. This DataGrid displays some table. Table contains 1 column with a Date. This Date property is represented as a string. In most cases, Date has some value that we read from the database. But in some cases user can create a new record in this table. In this case, the Date should be null (with a "null" value, not a String.Empty). And it should be written to the database as null. Also the cell in the table for "null" Date should be empty. So it's why we use for it a string type.

The DataGrid ItemSource property has binding to the ICollectionView MyView property from the view model. Also, the view model contains other property related to my collection of data and is used for the MyView:

public class MyCustomObject
{
    public string Date { get; set; }
   
    // some other properties.
}

public class MyViewModel
{
    public ObservableCollection<MyCustomObject> TableItems
    {
       get { return tableItems; }
       set
       {
          Set(ref tableItems, value, true);
          TableView = CollectionViewSource.GetDefaultView(tableItems);
       }
    }

    public virtual ICollectionView TableView
    {
       get { return tableView; }
       set
       {
          Set(ref tableView, value);
       }
    }

    public MyViewModel()
    {
        var someCollection = new List<MyCustomObject>();
        TableItems = new ObservableCollection<MyCustomObject>(someCollection);
    }
}

In some method of MyViewModel I've added the sorting for my collection by ICollectionView.SortDescriptions. Like:

TableView.SortDescriptions.Add(new SortDescription(nameof(MyCustomObject.Date), ListSortDirection.Ascending));

All works. But as my Date is a string it's compared as a string. So I can get something like this:

02/10/19
03/09/19
03/10/20
04/08/17

How can I add some custom comparer to my TableView and change the sorting logic for some property? Should I have to add the custom comparer to the my someCollection in the constructor before adding it to the TableItems?


Solution

  • You must cast the ICollectionView to ListCollectionView.

    Depending on the actual collection, the view returned by CollectionViewSource.GetDefaultView is an implementation of ICollectionView.
    For collections of type IList this is the ListCollectionView.

    Also consider to use a DateTime instead of string to store the date.

    The following example creates a date string IComparer or an alternate DateTime IComparer and assigns it to the collection's view:

    var listCollectionView = (ListCollectionView)CollectionViewSource.GetDefaultView(this.TableItems);
    
    // To compare DateTime 
    Comparer<DateTime> dateTimeComparer = Comparer<DateTime>.Create(DateTime.Compare);
    
    // To compare date strings.
    // Optionally define the date culture for DateTime.Parse
    CultureInfo dateCulture = CultureInfo.CurrentCulture;
    
    Comparison<string> dateTimeStringComparison = (stringX, stringY) 
      => DateTime.Compare(DateTime.Parse(stringX, dateCulture), DateTime.Parse(stringY, dateCulture));
    Comparer<string> dateTimeStringComparer = Comparer<string>.Create(dateTimeStringComparison);
    
    listCollectionView.CustomSort = dateTimeStringComparer;