CollectionViewSource sorting only the first time it is bound to a source CollectionViewSource sorting only the first time it is bound to a source wpf wpf

CollectionViewSource sorting only the first time it is bound to a source


Great question and an interesting observation. Upon closer inspection, it appears that the DataGrid clears sort descriptions of a previous ItemsSource before a new one is set. Here is its code for OnCoerceItemsSourceProperty:

private static object OnCoerceItemsSourceProperty(DependencyObject d, object baseValue){    DataGrid grid = (DataGrid) d;    if ((baseValue != grid._cachedItemsSource) && (grid._cachedItemsSource != null))    {        grid.ClearSortDescriptionsOnItemsSourceChange();    }    return baseValue;}

This behavior only happens on a DataGrid. If you used a ListBox instead (to display the "Players" collection above), the behavior will be different and the SortDescriptions will still remain after selecting different items from the parent datagrid.

So I guess the solution to this is to somehow re-apply the sort descriptions of the Players collection whenever the selected item in the parent DataGrid (i.e. "lstLevel") changes.

However, I'm not 100% sure about this and probably needs more testing/investigation. I hope I was able to contribute something though. =)

EDIT:

As a suggested solution, you can put a handler for lstLevel.SelectionChanged in your constructor, before setting the lstLevel.ItemsSource property. Something like this:

lstLevel.SelectionChanged +=    (sender, e) =>    {        levels.ToList().ForEach((p) =>        {            CollectionViewSource.GetDefaultView(p.Players)                .SortDescriptions                .Add(new SortDescription("Name", ListSortDirection.Ascending));        });    };lstLevel.ItemsSource = levels;

EDIT2:

In response to the problems you're encountering with regards to keyboard navigation, I suggest that instead of handling the "CurrentChanged" event, you handle the lstLevel.SelectionChanged event instead. I'm posting the necessary updates you need to make below. Just copy-paste to your code and see if it works fine.

XAML:

<!-- Players data, with sort on the Name column --><StackPanel Grid.Column="1">    <Label>DataGrid:</Label>    <DataGrid Name="lstPlayers" AutoGenerateColumns="False"        CanUserSortColumns="False"        ItemsSource="{Binding ElementName=lstLevel, Path=SelectedItem.Players}">        <DataGrid.Columns>            <DataGridTextColumn Header="Name"                        Binding="{Binding Path=Name, Mode=TwoWay}"                        Width="*" />            <DataGridTextColumn Header="Age"                        Binding="{Binding Path=Age, Mode=TwoWay}"                        Width="80">            </DataGridTextColumn>        </DataGrid.Columns>    </DataGrid></StackPanel><StackPanel Grid.Column="2">    <Label>ListBox:</Label>    <ListBox ItemsSource="{Binding ElementName=lstLevel, Path=SelectedItem.Players}" DisplayMemberPath="Name" /></StackPanel>

Code-behind (constructor):

lstLevel.SelectionChanged +=    (sender, e) =>    {        levels.ToList().ForEach((p) =>        {            CollectionViewSource.GetDefaultView(p.Players)                .SortDescriptions                .Add(new SortDescription("Name", ListSortDirection.Ascending));        });    };lstLevel.ItemsSource = levels;


A better workaround:CollectionViewSource sorting only the first time it is bound to a source

Implement your own DataGrid:

public class SDataGrid : DataGrid{    static SDataGrid()    {        ItemsControl.ItemsSourceProperty.OverrideMetadata(typeof(SDataGrid), new FrameworkPropertyMetadata((PropertyChangedCallback)null, (CoerceValueCallback)null));    }}

The only thing coerce callback does in the current implementation is clear the sort descriptions. You can simply "cut" this code by overriding metadata. Not viable on Silverlight: OverrideMetadata API is not public. Though I'm not sure Silverlight is affected by this bug. Other risks and side effects may apply.


I was able to fix this by simply calling PropertyChanged on the property that exposes the view, letting the view refresh (and clear out the sort) and then adding the sort descriptions.