I have a hacked-together DataGrid with group subtotal rows in one of my projects. We weren't concerned about some of the issues you bring up, such as hiding and sorting columns so I don't know for sure if it can be extended for that. I also realize there could be performance issues that may be a problem with large sets (my window is operating 32 separate DataGrids - ouch). But it's a different direction from other solutions I've seen, so I thought I'd throw it up here and see if it helps you out.
My solution consists of 2 major components:
1. The subtotal rows aren't rows in the main DataGrid, but are separate DataGrids. I have 2 extra grids in each group actually: 1 in the header that is only displayed when the group is collapsed, and one beneath the ItemsPresenter. The ItemsSource for the subtotal DataGrids comes from a Converter that takes the items in the group and returns an aggregate view model. The columns of the subtotal grids are exactly the same as the main grid (filled out in DataGrid_Loaded, though I'm sure it could be done in xaml too).
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander Background="Gray" HorizontalAlignment="Left" IsExpanded="True"
ScrollViewer.CanContentScroll="True">
<Expander.Header>
<DataGrid Name="HeaderGrid" ItemsSource="{Binding Path=., Converter={StaticResource SumConverter}}"
Loaded="DataGrid_Loaded" HeadersVisibility="Row"
Margin="25 0 0 0" PreviewMouseDown="HeaderGrid_PreviewMouseDown">
<DataGrid.Style>
<Style TargetType="DataGrid">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Expander}, Path=IsExpanded}"
Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
</DataGrid>
</Expander.Header>
<StackPanel>
<ItemsPresenter/>
<DataGrid Name="FooterGrid" ItemsSource="{Binding ElementName=HeaderGrid, Path=ItemsSource, Mode=OneWay}"
Loaded="DataGrid_Loaded" HeadersVisibility="Row"
Margin="50 0 0 0">
<DataGrid.Style>
<Style TargetType="DataGrid">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Expander}, Path=IsExpanded}"
Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid>
</StackPanel>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
2. Then the issue is how to get all DataGrids to behave as if they were a single grid. I've handled that by subclassing DataGridTextColumn
(we only have text in this case, but other column types should work too) in a class called DataGridSharedSizeTextColumn
that mimics the SharedSizeGroup behavior of the Grid class. It has a string dependency property with a group name and keeps track of all columns in the same group. When Width.DesiredValue
changes in one column, I update the MinWidth in all the other columns and force an update with DataGridOwner.UpdateLayout()
. This class is also covering column reordering and does a group-wide update whenever DisplayIndex changes. I would think this method would also work with any other column property as long as it has a setter.
There were other annoying things to work out with selection, copying, etc. But it turned out to be pretty easy to handle with MouseEntered and MouseLeave events and by using a custom Copy command.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…