CollectionViewSource сортирует только первый раз, когда он связан с источником - PullRequest
18 голосов
/ 31 августа 2010

Я использую DataGrid, привязанный к CollectionViewSource ( Players ), сам привязанный к текущему выбранному элементу ListBox ( уровней ), каждый элемент содержит коллекцию, которая будетотсортировано / отображено в DataGrid:

<ListBox Name="lstLevel"
         DisplayMemberPath="Name" 
         IsSynchronizedWithCurrentItem="True" />

...

<!-- DataGrid source, as a CollectionViewSource to allow for sorting and/or filtering -->
<CollectionViewSource x:Key="Players" 
                      Source="{Binding ElementName=lstLevel, 
                                       Path=SelectedItem.Players}">
  <CollectionViewSource.SortDescriptions>
    <scm:SortDescription PropertyName="Name" />
  </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

...

  <DataGrid Name="lstPlayers" AutoGenerateColumns="False" 
            CanUserSortColumns="False"
            ItemsSource="{Binding Source={StaticResource 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>

(весь код C # здесь , Код XAML здесь , весь тестовый проект здесь - в дополнение к DataGrid я добавил простой ListBox для игроков, чтобы убедиться, что это не проблема DataGrid)

Проблема в том, что игроки сортируются при первом их отображении, но как только я выбираю другой уровень в ListBox, они больше не сортируются.Кроме того, изменение имен при первом отображении игроков будет сортировать их в соответствии с изменениями, но не после того, как уровень был изменен.

Таким образом, похоже, что изменение источника CollectionViewSource каким-то образом нарушает функцию сортировки,но я понятия не имею, почему и как это исправить.Кто-нибудь знает, что я делаю не так?

(я сделал тест с фильтром, но он продолжал работать как положено)

Фреймворк .NET 4.

Ответы [ 3 ]

13 голосов
/ 31 августа 2010

Отличный вопрос и интересное наблюдение. При ближайшем рассмотрении выясняется, что 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 (для отображения коллекции «Players» выше), поведение будет другим, и описания SortDescription останутся после выбора других элементов из родительской таблицы данных.

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

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

EDIT:

В качестве предлагаемого решения вы можете поместить обработчик для 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;
5 голосов
/ 12 июня 2013

Мне удалось это исправить, просто вызвав PropertyChanged для свойства, предоставляющего представление, позволив обновлению представления (и очистив сортировку), а затем добавив описания сортировки.

4 голосов
/ 05 апреля 2012

лучший обходной путь: CollectionViewSource сортирует только при первом подключении к источнику

Реализация собственной DataGrid:

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

Единственное, что coerce callback делает в текущей реализации, это очистить описания сортировки. Вы можете просто «вырезать» этот код переопределение метаданных. Недоступно в Silverlight: API-интерфейс OverrideMetadata не является публичным Хотя я не уверен, что это влияет на Silverlight ошибка. Другие риски и побочные эффекты могут применяться.

...