对DataGrid组进行排序

时间:2014-10-16 16:55:25

标签: c# wpf sorting datagrid grouping

我有一个非常基本的DataGrid包含一些测试数据。数据以ObservableCollection<T> Client的{​​{1}}提供,其中包含objectsOrganizationFirstName属性。我按LastName对项目进行分组,并希望能够按特定群组中的OrganizationFirstName进行排序(此部分正常运行),并按{{1}对群组进行排序}。最后一部分是我遇到问题的部分。

如果我没有指定任何排序属性,或者我在LastName中指定了一个属性(见下文),例如OrganizationCollectionViewSource.SortDescriptions,{{1除外它会让我做一个初步的排序。如果您查看下面的图像,公司名称将不按顺序显示,因为这是集合的初始化方式。如果我点击PropertyName="Name"标题,它会按升序对其进行排序,但不会让我在此之后再进行排序。如果我再次单击标题,它将更改箭头,这表示排序方向,但不会发生实际排序。无论我指定什么排序属性,这都是相同的...即使是完全任意的属性,除非它PropertyName="CompleteNonsense",或者没有指定属性。

如果我在PropertyName="Organization"上指定排序,它将按升序开始排序,但如果单击标题则不会排序。因此,似乎该属性名称确实触发排序,但仅限于第一次。

截图:

这里是Organization的{​​{1}}:

PropertyName="Organization"

这里是PropertyName="Organization"XAML的{​​{1}}:

DataGrid

有没有人知道如何在<DataGrid ItemsSource="{Binding Source={StaticResource GroupedData}}" AutoGenerateColumns="False" IsReadOnly="True" GridLinesVisibility="None" HeadersVisibility="Column" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="False" Background="White"> <DataGrid.CellStyle> <Style TargetType="DataGridCell"> <Setter Property="BorderThickness" Value="0"/> </Style> </DataGrid.CellStyle> <DataGrid.RowStyle> <Style TargetType="DataGridRow"> <Setter Property="Margin" Value="10,0,0,0"/> </Style> </DataGrid.RowStyle> <DataGrid.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <DockPanel Background="LightBlue" DataContext="{Binding Items}"> <TextBlock Text="{Binding Path=Organization}" Foreground="Blue" Margin="5,0,0,0" Width="100"/> <TextBlock Text="Employee Count:" Foreground="Blue" Margin="40,0,0,0"/> <TextBlock Text="{Binding Path=Count}" Foreground="Blue" Margin="5,0,0,0"/> </DockPanel> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </DataGrid.GroupStyle> <DataGrid.Columns> <DataGridTextColumn Header="Organization" Binding="{Binding Organization}"/> <DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/> <DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/> <DataGridTextColumn Width="*"/> </DataGrid.Columns> </DataGrid> 超出初始排序后对组进行排序?

修改

我一直在搞乱这个问题(真的需要弄明白这一点)并为标题创建了一个快速的代码隐藏事件处理程序。 XAML事件:

Grouping


Sorting

每次点击<CollectionViewSource Source="{Binding Data}" x:Key="GroupedData"> <CollectionViewSource.GroupDescriptions> <PropertyGroupDescription PropertyName="Organization"/> </CollectionViewSource.GroupDescriptions> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="Organization"/> </CollectionViewSource.SortDescriptions> </CollectionViewSource> 后,弹出Organization,向下排序箭头点,点击Click后,它会立即恢复向上指向。组的实际排序永远不会在视觉上发生。有些东西让它卡在<DataGrid.ColumnHeaderStyle> <Style TargetType="DataGridColumnHeader"> <EventSetter Event="Click" Handler="ColumnHeader_Click"/> </Style> </DataGrid.ColumnHeaderStyle> 订单中。

1 个答案:

答案 0 :(得分:4)

注意:有关最新代码,请参阅修改

这有点棘手,但我能够找到答案。下面的代码适用于Sorting事件的代码隐藏处理程序,但可以轻松调整以放置在ViewModelView中(取决于您的操作方式)。< / p>

<强>代码:

<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排序,每个群组中的项目按FirstNameLastName排序。希望这段代码可以帮助别人。我一整天都在搜索,这似乎是人们在DataGrid处理群组时遇到的常见问题。

唯一的缺点是当组项目按分组属性以外的任何其他方式排序时,它会将组的顺序重置为默认值。在尝试了很多代码后,我无法找到解决方案。如果有人找到该部分的解决方案,我很乐意给他们“正确答案”。

帮助我的一些资源:

How to force DataGrid group order in WPF?
WPF Datagrid group and sort

修改

找出关于通过组内排序重置的组间排序的最后部分。这有点乱,因为我没有时间来清理它,但我想我会分享我的代码:

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