WPF Datagrid: ListCollectionView sorting does not seem to work after Data is changed

2.1k Views Asked by At

My DataGrid's ItemsSource is a ListCollectionView, which has a GroupDescription and two SortDescriptions. On the first run of the program, everything works as it should.

However, when the data the ListCollectionView is based on gets changed, the sorting of the ListCollectionView fails (whereas the Grouping does not). It behaves as if the two lines marked as "LINE A" and "LINE B" were not there at all.

public List<MyModel> Models { get; set; }
public ListCollectionView _collectionView;

public MyConstructor() 
{
    InitializeComponent();
    GetData();
    Grouping();
}

public void GetData() 
{
    // fill the list "Models"
}

public void Grouping()
{
    // _collectionView = null;
    _collectionView = new ListCollectionView(Models);
    _collectionView.GroupDescriptions.Add(new PropertyGroupDescription("MyModelSupplier"));
    _collectionView.SortDescriptions.Add(new SortDescription("MyModelSupplier", ListSortDirection.Ascending)); // LINE A
    _collectionView.SortDescriptions.Add(new SortDescription("MyModelName", ListSortDirection.Ascending)); // LINE B
    ModelControl.ItemsSource = _collectionView;
}

private void OnDataChanged (object sender, EventArgs e)
{
    // Save the Data to the Database
    //...

    // Retrieve the Data
    GetData();

    // Group it again
    Grouping();
}

To summarize:

On startup everything looks and behaves like it should. Changing data, saving it to the DataBase and retrieving it again does work also. Just the sorting of the retrieved, grouped data won't work (the list is sorted - like default - by the first property of the MyModel class, which is MyModelName ... and NOT by MyModelSupplier, as it does in the very beginning).

Edit: This is a known bug/missing feature (up until .NET 4.6.2). Several suggestions can be found here (I went with the last solution):

Bug-Report: https://connect.microsoft.com/VisualStudio/feedback/details/2017716/wpf-live-shaping-groups-are-not-sorted-correctly-after-a-property-changes

Simple Fix: https://stackoverflow.com/a/10121983/8187945

2

There are 2 best solutions below

4
XAMlMAX On

I am unsure why you are making your ListCollectionView in your VM as this is a part of PresentationFramework.dll so no MvvM in here.
However if you use CollectionViewSource in your xaml then this would be much easier!
in your xaml you would do:

<CollectionViewSource Source="{Binding Items}" x:Key="items">
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="yourPropertyHere"/>
    </CollectionViewSource.GroupDescriptions>
    <CollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="propertyHere" />
    </CollectionViewSource.SortDescriptions>
    <CollectionViewSource.LiveFilteringProperties>
        propertyNameHere
    </CollectionViewSource.LiveFilteringProperties>
</CollectionViewSource>  

Where:

xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"  

and then use it for your DataGrid like so:
<DataGrid ItemsSource="{Binding Source={StaticResource events}}"/>

This gives you the ability to have these options in xaml and away from your view model.
You can also do this in code behind:
XAML:

<CollectionViewSource x:Key="MyCVS"
                          Source="{Binding Items}"
                          Filter="My_Filter" />  

and then in your code behind:

void My_Filter(object sender, FilterEventArgs e)
{
    var item = e.Item as yourModelObject;
    if (item == <your test here>)
    {
        e.Accepted = true;
    }
    else
    {
        e.Accepted = false;
    }
}  

If you still have difficulties with the view not being refreshed then you could use this in your code behind to access the items being displayed and just refresh:

CollectionViewSource.GetDefaultView(lst.ItemsSource).Refresh();// where lst would be ListView or DataGrid
1
Tzwenni On

Honestly, I wasted six hours of my lifetime. SortDescriptions just worked for 1st time and I wasn't able to implement all offered solutions I found somewhere.

I decided to use CustomSort. After five minutes I was finished. So, if you read this, maybe you'll save time

The Solution 1 Part:

public class MyComparer : IComparer
{
    public int Compare(object x, object y)
    {
        var a = x as CustomViewEntityViewModel;
        var b = y as CustomViewEntityViewModel;

        if (a == null || b == null)
            throw new ArgumentException("Not My CustomViewEntityViewModel");

        if (a.SortOrder > b.SortOrder)  // I added property SortOrder to my viewmodel
            return 1;
        if (a.SortOrder == b.SortOrder)
            return 0;

        return -1;
    }
}

2nd Part: How to use

var myListCollectionView = (ListCollectionView)CollectionViewSource.GetDefaultView(Filters);
myListCollectionView.CustomSort = new MyComparer();
myListCollectionView.GroupDescriptions.Add(new PropertyGroupDescription("Category"));

MyListCollectionView = myListCollectionView; // Which is binded by DG.ItemsSource