2012-08-16 11 views
8

私はWPF DataGridを持っています。カラムヘッダーをクリックすることで並べ替えることができます。それは動作しますが、それは不安定です。どのようにして安定したソートを行うのですか?安定した並べ替えを使用して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

+0

コードの背後でこれを行う方法を探していますか、誰かがshift-click-behaviourをデフォルトの動作にする方法を知りたいと思っていますか? – Grubsnik

+0

@ Grubsnik Shiftキーを押しながらアプローチするのが大きな問題だと思うので、誰かがソートアルゴリズムを変更する方法を知りたいと思っていました。しかし、それは仕事をします。 – TarkaDaal

答えて

1

私は私を渡す必要がある私のカスタムの比較子を、設定したListCollectionViewのCustomSortプロパティを使用して...

を私はカスタム比較子を使用して安定したソートを得ることができたが、それはちょっと大きなハックのように感じていますそれをインスタンス化するときにコレクションを作成します。私のカスタム比較演算で

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

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

、私は定期的な比較はゼロを返したときだけ(それらが同じであるか、同じ値を持つ)の項目のインデックスにフォールバックするCompareメソッド中にコレクションを使用しています。

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内のコレクションのプロパティを維持することになります。または、2つの並べ替え方向をサポートしています(これで作業するのは難しくありません)。または、パフォーマンスがどのように機能するかを確認してください。

しかし、私はその考えがはっきりしていると思います。私が言ったように、ハッキーだが。

8

はこれを参照してください。 クラス列をクリックしてから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)); 
     } 
    } 
} 

は、ここでの背後にあるコードでソート追加するためのソリューションです。 hbarckさんのコメントを読んだ後、私は再びあなたの質問を読んで、そして私はいくつかの部分を逃したようだ :

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

編集:そうのようにこれを行う1つの方法は、ソートのイベントを停止することです。これに

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キーを押したままにすると、クリックされた列でソートする前に前のソートをクリアします。

+0

+1。これは素晴らしいです、ありがとう!それは短期間で便利です。しかし、私はそれを答えとしてまだマークしたくありません。理想的には、私はデフォルトでこの動作を望みます(シフトを保持せずに)。私はその質問を明確にするでしょう。 – TarkaDaal

+1

@TarkaDaalどのように以前のソートを "削除"しますか?クラスと生徒別にソートしたいが、クラスとグレードでソートしたいとします。最初にソートする新しい列を設定するいくつかの方法を実装する必要があります。たとえば、Shiftキーを押しながらクリックすると、;;) – Eirik

+0

する必要はありません。 Class、Gradeでソートする場合は、Classをクリックし、Gradeをクリックします。学生の順序は関係ありません。これは、複数の列で並べ替えることではなく、可能な限り以前の順序を保持することです。 – TarkaDaal

関連する問題