【发布时间】:2017-04-11 06:13:16
【问题描述】:
我有一个由数据库查询填充的 WPF DataGrid。初始结果在 SQL 中进行分组排序:
我希望我的用户能够按任意列排序,但简单地按“值”排序会混淆组。我希望组保持分组,同时按指定列排序。
例如,按“值”列升序排序,应按每个组中的最小“值”对组进行排序。结果应该是:
我已经有一个排序处理程序在工作,我怀疑 Linq 在这里可能有用,但我似乎找不到对 Value 和 GroupName 进行排序的方法。
【问题讨论】:
我有一个由数据库查询填充的 WPF DataGrid。初始结果在 SQL 中进行分组排序:
我希望我的用户能够按任意列排序,但简单地按“值”排序会混淆组。我希望组保持分组,同时按指定列排序。
例如,按“值”列升序排序,应按每个组中的最小“值”对组进行排序。结果应该是:
我已经有一个排序处理程序在工作,我怀疑 Linq 在这里可能有用,但我似乎找不到对 Value 和 GroupName 进行排序的方法。
【问题讨论】:
这是一个小例子,如何根据需要进行分组,你可以更改它,你的列表来自 db,你可以像我一样做,不要忘记 IComparable
static void Main(string[] args)
{
List<MyClass> inputlist = new List<MyClass>();
inputlist.Add(new MyClass { GroupName = "A", Value = 10 });
inputlist.Add(new MyClass { GroupName = "A", Value = 15 });
inputlist.Add(new MyClass { GroupName = "A", Value = 20 });
inputlist.Add(new MyClass { GroupName = "B", Value = 1 });
inputlist.Add(new MyClass { GroupName = "B", Value = 10 });
inputlist.Add(new MyClass { GroupName = "B", Value = 15 });
inputlist.Add(new MyClass { GroupName = "C", Value = 5 });
inputlist.Add(new MyClass { GroupName = "C", Value = 10 });
inputlist.Add(new MyClass { GroupName = "C", Value = 15 });
List<MyClass> outputlist = new List<MyClass>();
foreach (var item in
inputlist.GroupBy(x => x.GroupName).Select(x => new MyClass
{
GroupName = x.First().GroupName,
Value = x.Min().Value
}).ToList().OrderBy(x => x.Value))
{
outputlist.AddRange(inputlist.Where(x => x.GroupName == item.GroupName));
}
foreach (var item in outputlist)
{
Console.WriteLine(item.GroupName + " " + item.Value);
}
Console.ReadLine();
}
}
public class MyClass : IComparable
{
public string GroupName { get; set; }
public int Value { get; set; }
public int CompareTo(object value)
{
int val = (int)Value;
if (this.Value > val) return -1;
if (this.Value == val) return 0;
return 1;
}
}
【讨论】:
您确实需要通过以下方式实现排序处理程序(参见代码中的 cmets):
private void OnSorting(object sender, DataGridSortingEventArgs e) {
if (e.Column.SortMemberPath == "Value") {
// get view
var source = (ListCollectionView) CollectionViewSource.GetDefaultView(this.Items);
// manually change sort direction to the next value
// so null > ascending > descending > back to null
var sort = e.Column.SortDirection;
if (sort == null)
sort = ListSortDirection.Ascending;
else if (sort == ListSortDirection.Ascending)
sort = ListSortDirection.Descending;
else
sort = null;
if (sort != null) {
// first figure out correct group ordering
var sortedGroups = dataGrid.ItemsSource.OfType<Item>()
.GroupBy(c => c.GroupName)
.Select(c => new {GroupName = c.Key, MinValue = c.Min(r => r.Value)})
.OrderBy(c => c.MinValue)
.Select(c => c.GroupName)
.ToArray();
// now set collection view custom sort to out comparer
source.CustomSort = new ItemComparer(sortedGroups, sort == ListSortDirection.Ascending);
}
else {
// otherwise remove custom sort and sort as usual
source.CustomSort = null;
}
e.Column.SortDirection = sort;
e.Handled = true;
}
}
public class ItemComparer : IComparer {
private readonly string[] _sortedGroups;
private readonly bool _asc;
public ItemComparer(string[] sortedGroups, bool asc) {
_sortedGroups = sortedGroups;
_asc = asc;
}
public int Compare(object ox, object oy) {
var x = (Item) ox;
var y = (Item) oy;
if (x.GroupName == y.GroupName) {
// if group names are the same - sort as usual, by Value
return x.Value.CompareTo(y.Value) * (_asc ? 1 : -1);
}
// otherwise - sort by group name using the order we already figured out at previous step
return (Array.IndexOf(_sortedGroups, x.GroupName) - Array.IndexOf(_sortedGroups, y.GroupName)) * (_asc ? 1 : -1);
}
}
【讨论】: