19

CollectionViewSource(players)にバインドされたDataGridを使用しており、それ自体がListBox(levels)の現在選択されているアイテムにバインドされています。各アイテムには、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に加えて、DataGridの問題ではないことを確認するために、プレーヤー用の単純なリストボックスを追加しました)

問題は、プレーヤーが最初に表示されたときに並べ替えられることですが、リストボックスから別のレベルを選択するとすぐに並べ替えられなくなります。また、プレイヤーが最初に表示されたときに名前を変更すると、変更に応じて名前が並べ替えられますが、レベルが変更されると、名前は並べ替えられなくなります。

したがって、CollectionViewSourceのソースを変更すると、並べ替え機能が機能しなくなるように見えますが、その理由も修正方法もわかりません。誰かが私が間違っていることを知っていますか?

(私はフィルターを使ってテストを行いましたが、それは期待どおりに機能し続けました)

フレームワークは.NET4です。

4

3 に答える 3

15

素晴らしい質問と興味深い観察です。よく調べてみると、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」コレクションを表示するため)、動作は異なり、SortDescriptions は親データグリッドから別のアイテムを選択した後も残ります。

したがって、これに対する解決策は、親 DataGrid (つまり「lstLevel」) で選択された項目が変更されるたびに、Players コレクションの並べ替えの説明を何らかの形で再適用することだと思います。

ただし、これについて 100% 確信があるわけではなく、おそらくさらにテスト/調査が必要です。少しでも貢献できれば幸いです。=)

編集:

推奨される解決策として、lstLevel.ItemsSource プロパティを設定する前に、コンストラクターに lstLevel.SelectionChanged のハンドラーを配置できます。このようなもの:

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;
于 2010-08-31T16:24:29.937 に答える
5

より良い回避策: CollectionViewSource は、ソースに初めてバインドされたときにのみ並べ替えます

独自の DataGrid を実装します。

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

現在の実装で強制コールバックが行う唯一のことは、並べ替えの説明をクリアすることです。メタデータをオーバーライドすることで、このコードを簡単に「カット」できます。Silverlight では実行できません: OverrideMetadata API は公開されていません。Silverlight がこのバグの影響を受けるかどうかはわかりませんが。他のリスクと副作用が適用される場合があります。

于 2012-04-04T20:56:20.450 に答える
5

ビューを公開するプロパティで PropertyChanged を呼び出し、ビューを更新 (および並べ替えをクリア) し、並べ替えの説明を追加するだけで、これを修正できました。

于 2013-06-12T15:28:44.487 に答える