如何处理组小计,例如WPF DataGrid 中的目标行?

Posted

技术标签:

【中文标题】如何处理组小计,例如WPF DataGrid 中的目标行?【英文标题】:how to handle group subtotal and e.g. target rows in WPF DataGrid? 【发布时间】:2018-02-22 16:54:00 【问题描述】:

我正在实现一个 WPF DataGrid,其中包含具有许多关键数据的项目。项目按项目类别分组。

每个类别应该有:

    在每个关键值列中显示该列所有行的总和的行。 不属于绑定到的数据源网格的目标行。目标行告诉每一列当年的目标是什么(例如要花多少钱)。

这些行应始终在每个组的顶部(排序过滤)。

我的第一个解决方案是将这些数据放在组标题中。这不是一个好的解决方案,因为组标题不支持列。即它应该通过获取列宽来构造。

这是可以做到的,但是当用户想要重新排序和隐藏列时会变得复杂。

DataGrid 正在使用 CollectionViewSource,因此它没有填充 C# 代码。基本上我正在扩展这个例子:http://msdn.microsoft.com/en-us/library/ff407126.aspx

感谢和最好的问候 - 马蒂

【问题讨论】:

你有没有找到任何方法来满足这个需求?这是我的 SO 问题(我标记为重复):***.com/questions/46206673/… 【参考方案1】:

在我的一个项目中,我有一个组合小计行的组合 DataGrid。我们并不关心您提出的一些问题,例如隐藏和排序列,所以我不确定它是否可以扩展。我也意识到可能存在大型集合可能存在的性能问题(我的窗口正在运行 32 个单独的 DataGrids - 哎哟)。但这与我见过的其他解决方案的方向不同,所以我想我会把它扔在这里,看看它是否对你有帮助。

我的解决方案由 2 个主要部分组成: 1.小计行不是主DataGrid中的行,而是单独的DataGrid。实际上,我在每个组中有 2 个额外的网格:1 个在标题中,仅在组折叠时显示,一个在 ItemsPresenter 下方。小计 DataGrids 的 ItemsSource 来自一个 Converter,它获取组中的项目并返回一个聚合视图模型。小计网格的列与主网格完全相同(在 DataGrid_Loaded 中填写,但我确信它也可以在 xaml 中完成)。

            <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. 然后问题是如何让所有 DataGrids 表现得好像它们是一个网格一样。我已经通过在模拟 Grid 类的 SharedSizeGroup 行为的名为 DataGridSharedSizeTextColumn 的类中子类化 DataGridTextColumn(在这种情况下我们只有文本,但其他列类型也应该工作)来处理这个问题。它有一个带有组名的字符串依赖属性,并跟踪同一组中的所有列。当Width.DesiredValue 在一列中更改时,我会更新所有其他列中的 MinWidth 并强制使用DataGridOwner.UpdateLayout() 进行更新。此类还涵盖列重新排序,并在 DisplayIndex 更改时进行组范围的更新。我认为这个方法也适用于任何其他列属性,只要它有一个 setter。

在选择、复制等方面还有其他烦人的事情需要解决。但事实证明,使用 MouseEntered 和 MouseLeave 事件以及使用自定义复制命令很容易处理。

【讨论】:

很抱歉没有检查你的答案。我希望它可以帮助其他人。【参考方案2】:

一种选择可能是在数据源中添加具有特殊值的行,用于 Name 和其他没有意义的字段,并使用 DataTrigger 以特殊颜色或其他颜色显示它们。

过滤是在 C# 中完成的,所以它不会影响这些行。

排序是这里唯一的问题。只是告诉某些行在组中的顺序为 0 和顺序为 1,那就太酷了。但是因为我不知道该怎么做,所以我必须在 C# 中对所有列进行自定义排序,而不是仅仅声明排序:

<CollectionViewSource.SortDescriptions>
   <!-- Requires 'xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"' declaration. -->
    <scm:SortDescription PropertyName="ProjectName"/>
    <scm:SortDescription PropertyName="Complete" />
    <scm:SortDescription PropertyName="DueDate" />
</CollectionViewSource.SortDescriptions>

编辑:除此之外,与我的第一个解决方案(组标题中的总和信息)相比,它有一个主要缺点,因为在过滤更改时,我应该更新仅针对可见行计算的总和。

所以这个答案是一个完整的黑客,缺乏所有的优雅,并且没有使用 WPF 应该有的好功能:(

【讨论】:

以上是关于如何处理组小计,例如WPF DataGrid 中的目标行?的主要内容,如果未能解决你的问题,请参考以下文章

主机网卡如何处理组播MAC地址

如何用代码选中datagrid中的某一行

如何处理来自 Presenter 的 Datagrid 列的事件?

如何处理 WPF 中的鼠标滚轮单击事件?

wpf datagrid中如何获取选中行中模板如textbox,combobox的内容

如何用多个选定的文件名填充WPF DataGrid以进行编辑?