2012-08-16 109 views
8

我有一個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

+0

您是否正在尋找一種方法在後面的代碼中執行此操作,或者希望有人能夠知道將shift-click-behavior設置爲默認行爲的方法? – Grubsnik 2012-09-10 11:54:58

+0

@Grubsnik我希望有人會知道如何改變排序算法,因爲我認爲shift-click方法是一個重大的黑客攻擊。但是,它完成了這項工作。 – TarkaDaal 2012-09-12 08:46:19

回答

1

我已經成功使用自定義比較,以獲得穩定的排序,但它有點感覺像一個大劈...

我用的ListCollectionView的CustomSort屬性設置我的自定義比較,這需要我通過在實例化它時收集到它。

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

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

在我的自定義比較,我用的比較方法,在收集剛剛退卻到的項目索引時經常比較返回零(它們是相同的或具有相同的值)。

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; 
    } 
} 

我還在測試這個,所以我不能保證這會工作所有的時間......一個問題會保持比較程序更新裏面的集合屬性,例如。或者支持兩種排序方向(現在開展工作,不應該很難)。或者檢查它是如何工作的,在性能方面。

但我認爲這個想法很清楚,雖然哈克,就像我說的。

8

您應該能夠通過多列排序列上點擊時按住shift。 嘗試單擊班級列,然後按住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)); 
     } 
    } 
} 

對於這個工作,你必須禁用標準排序。要做到這一點的方法之一是停止排序事件,像這樣:

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)); 

,你將有穩定的排序。點擊學生按學生排序,然後點擊班級按班級,學生排序。 如果按住ctrl鍵時清除先前排序,然後再按單擊列進行排序。

+0

+1。這很好,謝謝!它在短期內很方便。但是,我不想將它標記爲答案。理想情況下,我想默認情況下這種行爲(所以,沒有保持轉變)。我會澄清這個問題。 – TarkaDaal 2012-08-17 10:45:51

+1

@TarkaDaal你會如何「刪除」之前的排序?假設你想按班級和學生排序,但是你想按班級和年級排序。您需要實施一些設置新列的方法,例如先按住Shift鍵,然後單擊...;) – Eirik 2012-08-17 11:18:18

+0

您不需要。如果您想按Class排序,然後選擇Grade,只需點擊Class,然後點擊Grade。學生的順序無關緊要。這不是關於按多列進行排序,而是關於在可能的情況下保留以前的訂單。 – TarkaDaal 2012-08-17 15:38:05