9

私は WPF DataGrid を持っています。列ヘッダーをクリックして並べ替えることができるようにしています。動作しますが、不安定です。安定したソートを行うにはどうすればよいですか?

つまり、このテーブルがある場合:

Class    | Student    | Grade
-----------------------------
Art      | James      |  A
Art      | Amy        |  B
Art      | Charlie    |  A
Science  | James      |  D
Science  | Amy        |  A
Science  | Charlie    |  C
History  | James      |  B
History  | Amy        |  A
History  | Charlie    |  C

学生で並べ替えると、期待どおりに機能します。

Class    | Student    | Grade
-----------------------------
Art      | Amy        |  B
Science  | Amy        |  A
History  | Amy        |  A
Art      | Charlie    |  A
Science  | Charlie    |  C
History  | Charlie    |  C
Art      | James      |  A
Science  | James      |  D
History  | James      |  B

しかし、クラスごとに並べ替えると、次のようになります。

Class    | Student    | Grade
-----------------------------
Art      | James      |  A
Art      | Amy        |  B
Art      | Charlie    |  A
History  | James      |  B
History  | Amy        |  A
History  | Charlie    |  C
Science  | James      |  D
Science  | Amy        |  A
Science  | Charlie    |  C

生徒の並べ替え順序が破壊されています (不安定な並べ替え)。私が欲しいのは、順序を保持する安定した並べ替えです。

Class    | Student    | Grade
-----------------------------
Art      | Amy        |  B
Art      | Charlie    |  A
Art      | James      |  A
History  | Amy        |  A
History  | Charlie    |  C
History  | James      |  B
Science  | Amy        |  A
Science  | Charlie    |  C
Science  | James      |  D

デフォルトでこのように動作するか、少なくともトグルである必要があるようです。誰か提案はありますか?@Eirikのシフトクリックのアイデアは機能し、動作が存在することを示しています。ただし、私が本当に望んでいるのは、修飾子なしでそのように機能することです。「これ、次にこれ、次にこれ」の原因ではなく、アルゴリズムを別のアルゴリズムに交換する場合です。

これを参照してください: http://en.wikipedia.org/wiki/Sorting_algorithm#Stability

4

2 に答える 2

9

列をクリックするときにシフトを押し続けると、複数の列で並べ替えることができるはずです。クラスの列をクリックしてから、Shift キーを押しながら生徒の列をクリックしてみてください。

コードビハインドでソートを追加するためのソリューションは次のとおりです。

private void myDataGridPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    DependencyObject dep = (DependencyObject)e.OriginalSource;

    while ((dep != null) && !(dep is DataGridColumnHeader))
    {
        dep = VisualTreeHelper.GetParent(dep);
    }

    if (dep == null)
        return;

    if (dep is DataGridColumnHeader)
    {
        DataGridColumnHeader columnHeader = dep as DataGridColumnHeader;

        ICollectionView view = CollectionViewSource.GetDefaultView((sender as DataGrid).ItemsSource);

        if (columnHeader.Content.Equals("Class") || columnHeader.Content.Equals("Student"))
        {
            view.SortDescriptions.Clear();
            view.SortDescriptions.Add(new SortDescription("Class", ListSortDirection.Ascending));
            view.SortDescriptions.Add(new SortDescription("Student", ListSortDirection.Ascending));
        }
    }
}

これを機能させるには、標準の並べ替えを無効にする必要があります。これを行う 1 つの方法は、次のように Sorting イベントを停止することです。

private void myDataGridSorting(object sender, DataGridSortingEventArgs e)
{
    e.Handled = true;
}

編集:hbarckのコメントを読んだ後、あなたの質問をもう一度読みましたが、いくつかの部分を見逃していたようです. このコードを変更すると:

if (columnHeader.Content.Equals("Class") || columnHeader.Content.Equals("Student"))
{
    view.SortDescriptions.Clear();
    view.SortDescriptions.Add(new SortDescription("Class", ListSortDirection.Ascending));
    view.SortDescriptions.Add(new SortDescription("Student", ListSortDirection.Ascending));
}

これに:

if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{
    view.SortDescriptions.Clear();
}

view.SortDescriptions.Insert(0, new SortDescription(columnHeader.Content.ToString(), ListSortDirection.Ascending));

安定したソートが得られます。Student をクリックして Student で並べ替え、次に Class をクリックして Class, Student で並べ替えます。クリックするときに ctrl を押したままにすると、クリックされた列で並べ替える前に以前の並べ替えをクリアします。

于 2012-08-16T10:21:54.420 に答える
2

カスタム Comparer を使用して安定した並べ替えを行うことができましたが、大きなハックのように感じます...

ListCollectionView の CustomSort プロパティを使用してカスタム Comparer を設定します。これには、コレクションをインスタンス化するときにコレクションを渡す必要があります。

private void Sorting(IEnumerable collection)
{
    var view = CollectionViewSource.GetDefaultView(collection) as ListCollectionView;

    if (view != null)
    {
        view.CustomSort = new StableComparer(collection);
    }
}

私のカスタム Comparer では、Compare メソッド中にコレクションを使用して、通常の比較で 0 が返された (同じか同じ値を持っている) 場合にアイテム インデックスにフォールバックします。

public class StableComparer : IComparer
{
    public IEnumerable Collection { get; set; }

    public StableComparer(IEnumerable collection)
    {
        Collection = collection;
    }

    public int Compare(object x, object y)
    {
        IComparable x_Comparable = x as IComparable;
        IComparable y_Comparable = y as IComparable;

        if (x_Comparable != null && y_Comparable != null)
        {
            var comparison = x_Comparable.CompareTo(y_Comparable);

            // A zero value means x and y are equivalent for sorting, and they could
            //  be rearranged by an unstable sorting algorithm
            if (comparison == 0 && Collection != null)
            {
                // IndexOf is an extension method for IEnumerable (not included)
                var x_Index = Collection.IndexOf(x);
                var y_Index = Collection.IndexOf(y);

                // By comparing their indexes in the original collection, we get to
                //  preserve their relative order
                if (x_Index != -1 && y_Index != -1)
                    comparison = x_Index.CompareTo(y_Index);
            }

            return comparison;
        }

        return 0;
    }
}

私はまだこれをテストしているので、これが常に機能することを保証することはできません...たとえば、Comparer 内の Collection プロパティを更新し続けることが問題の 1 つです。または、2 つのソート方向をサポートします (現在取り組んでいますが、難しくはないはずです)。または、パフォーマンスに関して、これがどのように機能するかを確認します。

しかし、その考えは明確だと思います。私が言ったように、ハッキーですが。

于 2014-07-29T14:20:31.240 に答える