带分组的 WPF DataGrid 虚拟化
我正在使用 CodePlex 中的 WPF DataGrid,并且我需要让虚拟化与分组一起使用。
此问题与主题相关,并指向MSDN 示例,但它仅涵盖带有简单(即单个“列”)DataTemplate 的 ListControls。
分组和虚拟化似乎是网格的一个非常常见的用例。有没有标准/推荐/简单的方法来实现这一点?
I'm using the WPF DataGrid from CodePlex and I need to get Virtualization to work with grouping.
This question is on topic and points to an MSDN Example but it only covers ListControls with with simple (i.e. single 'column') DataTemplates.
Grouping and Virtualization seems like a pretty common use case for a Grid. Is there a standard/recommended/simple way of getting this going?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我意识到我迟到了……但我最近遇到了这个问题(使用 .NET 4 中内置的 DataGrid)。不幸的是,一旦在 DataGrid 上使用分组,行仍然没有虚拟化...但是我发现了一个非常巧妙的性能增强技巧,希望其他人也会发现它很有用。
假设您在 GroupItem 模板的扩展器中使用 ItemsPresenter,并且默认情况下您的扩展器未扩展,则尝试使用默认的 BooleanToVisibilityConverter 将 ItemsPresenter 的可见性绑定到 Expander 的 IsEnabled 属性
:您的 DataGrid 需要很长时间才能加载(因为它本质上是在绘制数据网格中的每条记录,即使它位于折叠的扩展器中)...然后使用上面的代码将导致数据网格在您展开组之前不会绘制您的记录,然后,它只会提取该特定组的记录。
缺点是,这仅在您的扩展器默认折叠时才有用,并且行仍然没有虚拟化(如果扩展组中有 100 个项目,但屏幕上只有 20 个项目,则所有 100 个项目都将在扩大群组的时间)。
好处是您基本上已经实现了 DataGrid 记录的延迟加载,因此直到您实际需要查看项目(您选择展开组)时才执行绘图工作。对于我的产品,我的组标题内置了按钮,用于对其组内的所有项目执行操作,因此用户通常不会扩展组,除非他们需要对组内的单个项目执行操作。
*如果您使用此技巧,需要注意的一件事是,您可能应该为列标题设置一些显式宽度或最小宽度(因为在 DataGrid 首次加载时不会绘制项目,因此列标题无法自动调整大小以适应最大的宽度)物品)。
希望真正的虚拟化能够在未来的服务包中实现,但如果没有,我希望这对其他人有帮助!
更新
看来这个问题将在.NET 4.5中通过新的附加属性来解决 VirtualizingPanel.IsVirtualizingWhenGrouping。
I realize I'm late to the party here... but I ran into this problem recently (using the DataGrid built into .NET 4). Unfortunately, there still is no virtualization of the rows once Grouping is used on the DataGrid... but a I found a very slick performance enhancement trick that hopefully somebody else will find useful as well.
Assuming you're using an ItemsPresenter within an expander of your GroupItem's template and by default your expander is not expanded, then try simply binding the visibility of your ItemsPresenter to the Expander's IsEnabled property with the default BooleanToVisibilityConverter:
If you're running into the problem where your DataGrid takes really long to load (because it's essentially drawing out every record in your datagrid even though it's in a collapsed expander)... then using the above code will cause the datagrid to not draw your records until you expand a group, and then, it will only draw out the records for that particular group.
The down side is that this only helps if your expanders are collapsed by default, and still the rows do not get virtualized (if you have 100 items in an expanded group, but only 20 fit on the screen, all 100 will be drawn at the time you expanded the group).
The upside is that you've essentially implemented lazy loading of your DataGrid records, so you're not performing the drawing work until you actually need to view the items (you choose to expand the group). For my product, my group header had buttons built in to perform operations on all items within its group, so more often the user never expanded a group unless they needed to perform an operation on an individual item within a group.
*One thing to note if you use this trick is that you should probably set some explicit widths or minimum widths to your column headers (because the items are not being drawn when the DataGrid first loads, so the column headers cannot autosize to fit the largest item).
Hopefully true virtualization gets implemented in a future service pack, but if not, I hope this will help somebody else!
Update
It appears this issue will be fixed in .NET 4.5 with a new attached property VirtualizingPanel.IsVirtualizingWhenGrouping.
Framework 4.5 中有一个新的附加属性 VirtualizingPanel.IsVirtualizingWhenGrouping,允许在分组时打开虚拟化。
There is a new attached property in framework 4.5 VirtualizingPanel.IsVirtualizingWhenGrouping, which allows to switch on virtualization when grouping.
在 ListView 或 DataGrid 中启用分组时,没有内置功能允许您启用 UI 虚拟化,如果您仔细考虑一下,这也是有道理的。 DataGrid 如何对不存在的项目进行分组。要应用分组,控件需要加载整个集合,这将破坏虚拟化的整个目的。
您可以做的最好的事情就是在您的视图模型(您再次绑定的对象)中提供某种虚拟化,因为您只提供当前需要的数据以及有关存在的数据量的某种通用数据,然后伪造自己的观点。
通过分组,它可能会像这样:当最初启用分组时,所有组都将被折叠。因此,您的视图模型只需为每个组提供一项。只是为了确保视图包含所有现有组。一旦用户展开一个组,ViewModel 就会动态地重新填充该组的项目。这是一种非常简单和基本的虚拟化方式,虽然不是最佳的,但它可能是一个很好的起点。这只是为了说明该方法。
There is no built-in feature that allows you to enable UI Virtualization when grouping is enabled in a ListView or DataGrid, If you think about it for a second it also makes sense. How is the DataGrid to group items that do not exist. To apply grouping the control would need to load the whole collection wich would defeat the whole purpose of virtualization.
The best you can probably do is to provide some sort of virtualization in your viewmodel (the object you bind agains) in that you provide only the data that is currently needed plus some sort of generic data about the amount of data that exists and then fake the view yourself.
With grouping it could go something like this: When grouping is enabled initially all groups would be collapsed. So your viewmodel would only have to provide one item for each group that there is. Just to make sure that the view contains all exisiting groups. As soon as the user expands one group the ViewModel would dynamically refill the items for that group. This is a very simple and basic way of virtulization and not optimal but it might be a good starting point to. It is just to illistrate the approach.
正如您所说,启用分组将禁用 UI 虚拟化。
我认为您不会找到解决此问题的简单方法。我建议您检查可用的 WPF DataGrid 之一,例如 XCeeed 可能在其控件中内置了此功能。
As you said, enabling Grouping will disable UI virtualization.
I don't think you'll find an easy way to solve this problem. I'd recommend you to check one of the available WPF DataGrid such as the XCeeed one that might have this feature built in their control.