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

I'm using a DataGrid bound to a CollectionViewSource (players), itself bound to the currently selected item of a ListBox (levels), each item containing a collection to be sorted/displayed in the DataGrid:


...



  
    
  

...

  
    
      
      
      
    
  

(whole C# code here, XAML code here, entire test project here - in addition to the DataGrid I've added a simple ListBox for the players, to make sure it wasn't a DataGrid issue)

The problem is that the players are sorted the first time they are shown, but as soon as I select another level from the ListBox, they are not sorted anymore. Also, modifying names the first time players are shown will sort them accordingly to the changes, but not anymore once the level has been changed.

So it looks like changing the source of the CollectionViewSource somehow breaks the sort feature, but I have no idea why, nor how to fix it. Does anyone know what I'm doing wrong?

(I did a test with a filter, but that one kept working as expected)

The framework is .NET 4.

19
задан RedGlyph 2 September 2010 в 09:44
поделиться

1 ответ

Отличный вопрос и интересное наблюдение. При ближайшем рассмотрении оказывается, что DataGrid очищает описания сортировки предыдущего ItemsSource перед установкой нового. Вот его код для OnCoerceItemsSourceProperty:

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

Такое поведение происходит только в DataGrid. Если вместо этого вы использовали ListBox (для отображения коллекции «Игроки» выше), поведение будет другим, и SortDescriptions по-прежнему останутся после выбора различных элементов из родительской сетки данных.

Поэтому я предполагаю, что решение этой проблемы состоит в том, чтобы каким-то образом повторно применять описания сортировки коллекции Players всякий раз, когда изменяется выбранный элемент в родительском DataGrid (т. е. "lstLevel").

Тем не менее, я не уверен в этом на 100% и, вероятно, нуждаюсь в дополнительных проверках/исследованиях. Я надеюсь, что все же смог внести свой вклад. =)

РЕДАКТИРОВАТЬ:

В качестве предлагаемого решения вы можете поместить обработчик для lstLevel.SelectionChanged в свой конструктор перед установкой свойства lstLevel.ItemsSource. Что-то вроде этого:

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

lstLevel.ItemsSource = levels;

EDIT2:

В ответ на проблемы, с которыми вы сталкиваетесь в отношении навигации с помощью клавиатуры, я предлагаю вместо обработки события "CurrentChanged" обрабатывать событие lstLevel.SelectionChanged. Я публикую необходимые обновления, которые вам нужно сделать ниже. Просто скопируйте и вставьте в свой код и посмотрите, нормально ли он работает.

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>

Код программной части (конструктор):

lstLevel.SelectionChanged +=
    (sender, e) =>
    {
        levels.ToList().ForEach((p) =>
        {
            CollectionViewSource.GetDefaultView(p.Players)
                .SortDescriptions
                .Add(new SortDescription("Name", ListSortDirection.Ascending));
        });
    };
lstLevel.ItemsSource = levels;
13
ответ дан 30 November 2019 в 04:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: