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 的 shift-clicking 想法有效,这表明行为存在。但是,我真正想要的是在没有任何修饰符的情况下那样工作。这不应该是“按此排序,然后按此排序,然后按此排序”的原因,而应该将算法换成不同的算法。

看到这个:http ://en.wikipedia.org/wiki/Sorting_algorithm#Stability

4

2 回答 2

9

您应该能够通过在单击列时按住 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));
        }
    }
}

为此,您必须禁用标准排序。一种方法是停止 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));

你会有稳定的排序。单击学生按学生排序,然后单击班级按班级、学生排序。如果在单击时按住 ctrl,则在按单击的列排序之前清除先前的排序。

于 2012-08-16T10:21:54.420 回答
2

我已经设法使用自定义比较器进行了稳定的排序,但这有点像一个大黑客......

我使用 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;
    }
}

我仍在测试这个,所以我不能保证它会一直工作......例如,一个问题是保持比较器中的 Collection 属性更新。或者支持两个排序方向(现在工作,应该不难)。或者在性能方面检查它是如何工作的。

但我认为这个想法很明确;虽然 hacky,就像我说的那样。

于 2014-07-29T14:20:31.240 回答