Note:参见Edit获取最新代码。
这有点棘手,但我找到了答案。下面的代码用于代码隐藏处理程序Sorting
事件,但可以轻松调整以放置在ViewModel
or View
(取决于你如何做事)。
Code:
<DataGrid Sorting="DataGrid_Sorting" ... > ... </DataGrid>
private void DataGrid_Sorting(object sender, DataGridSortingEventArgs e)
{
var headerName = "Organization";
var column = e.Column;
if (!column.Header.ToString().Equals(headerName))
{
return;
}
var source = (sender as System.Windows.Controls.DataGrid).ItemsSource as ListCollectionView;
if (source == null)
{
return;
}
e.Handled = true;
var sortDirection = column.SortDirection == ListSortDirection.Ascending ?
ListSortDirection.Descending : ListSortDirection.Ascending;
using (source.DeferRefresh())
{
source.SortDescriptions.Clear();
source.SortDescriptions.Add(new SortDescription(headerName, sortDirection));
}
source.Refresh();
column.SortDirection = sortDirection;
}
使用上面的代码,组本身按以下方式排序Organization
每组中的项目按以下顺序排序FirstName
and LastName
。希望这段代码对其他人有帮助。我一整天都在四处寻找,这似乎是人们在与群体打交道时面临的一个常见问题DataGrid
.
唯一的缺点是,当组项目按分组属性以外的任何方式排序时,它会将组的顺序重置为默认值。在尝试了很多代码之后,我一直无法找到解决方案。如果有人找到该部分的解决方案,我很乐意给他们“正确答案”。
一些对我有帮助的资源:
如何在 WPF 中强制 DataGrid 组顺序? https://stackoverflow.com/a/10365727/2006048
WPF Datagrid 分组和排序 https://stackoverflow.com/a/16054823/2006048
EDIT:
弄清楚了关于组间排序被组内排序重置的最后一部分。这有点乱,因为我没有时间清理它,但我想我应该分享我的代码:
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Organization"/>
</CollectionViewSource.GroupDescriptions>
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="Organization" Direction="Ascending"/>
<scm:SortDescription PropertyName="FirstName" Direction="Ascending"/>
<scm:SortDescription PropertyName="LastName" Direction="Ascending"/>
</CollectionViewSource.SortDescriptions>
private void DataGrid_Sorting(object sender, DataGridSortingEventArgs e)
{
SortGroupedDataGrid("Organization", sender, e);
}
private void SortGroupedDataGrid(string groupHeader, object sender, DataGridSortingEventArgs e)
{
// Get the main ListCollectionView and make sure it's not null.
var source = (sender as System.Windows.Controls.DataGrid).ItemsSource as ListCollectionView;
if (source == null)
{
return;
}
// Mark event as handled, so that automated sorting would not take place.
e.Handled = true;
// Main header which was used for the grouping. I'm only using one, but more can be added.
var headerName = groupHeader;
// Get the column that was being sorted on.
var column = e.Column;
// Check if the column was the same as the one being used for the grouping.
// I remove spaces so that any properties I use match the headers. Regex would probably
// work just as well, but it's an overkill for me at this time.
var isMainHeader = column.Header.ToString().Replace(" ", "").Equals(headerName);
// Because I set the initial sorting for all the properties in the XAML to be
// be sorted in Ascending order, I set these ones to Descending. One is for
// the main column and the other is for the secondary column. This does not account
// for a case where user Shift + Clicks multiple columns to chain sort.
var mainSortDirection = ListSortDirection.Descending;
var secondarySortDirection = ListSortDirection.Descending;
// If this is a main column sort event...
if (isMainHeader)
{
// Check its sorting direction and set it as opposite.
mainSortDirection = column.SortDirection == ListSortDirection.Descending ?
ListSortDirection.Ascending : mainSortDirection;
}
else
{
// ...else, get the sorting direction of the main column, because we want
// it to stay the same, and get the opposite sorting direction for the
// secondary column.
mainSortDirection = source.SortDescriptions[0].Direction;
secondarySortDirection = column.SortDirection == ListSortDirection.Descending ?
ListSortDirection.Ascending : secondarySortDirection;
}
// Defer refreshing of the DataGrid.
using (source.DeferRefresh())
{
// Clear any existing sorts. I've had some issues trying to alter existing ones.
source.SortDescriptions.Clear();
// Since we want main column to either alter if its sort event was called, or
// stay the same if secondary column event was called, we always set it first.
source.SortDescriptions.Add(new SortDescription(headerName, mainSortDirection));
// If this was not a main column event...
if (!isMainHeader)
{
// ...then set sorting for that other column. Since it'll be at index 1,
// after the main one, it'll only sort within each group, as I wanted.
source.SortDescriptions.Add(new SortDescription(column.Header.ToString().Replace(" ", ""), secondarySortDirection));
// Set the header direction as well.
column.SortDirection = secondarySortDirection;
}
else
{
// Otherwise, it's a main event and we want to show the error for its header.
// If you want primary sorting direction to always display, then simply take
// it outside of else scope, so that it's always assigned.
column.SortDirection = mainSortDirection;
}
}
// Now we can refresh and post changes.
source.Refresh();
}