WPF TreeView 虚拟化

18

我正在尝试理解虚拟化功能,但我不确定是否理解有误或出了什么问题。我使用ANTS内存分析工具检查虚拟树视图中的项数,但它一直在增加。我有一个包含1,001个项(1个根,1000个子项)的树视图,但我总是得到1,001个TreeViewItems、1,001个ToggleButtons和1,001个TextBlocks。虚拟化不应该重复使用这些项吗?如果是这样,为什么会有1,001个每种项?此外,CleanUpVirtualizedItem从未触发。

如果我理解错误,请告诉我,并提供如何使用的资源。我已经在互联网上搜索过,但没有找到有用的内容。

编辑:

即使我展开并滚动所有项,树使用的内存也从大约4MB增长到12MB。

谢谢您告知我。

这是我的代码。

XAML:

<Window x:Class="RadTreeViewExpandedProblem.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <TreeView x:Name="treeView"
                  VirtualizingStackPanel.IsVirtualizing="True"
                  VirtualizingStackPanel.CleanUpVirtualizedItem="TreeView_CleanUpVirtualizedItem">
            <TreeView.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel />
                </ItemsPanelTemplate>
            </TreeView.ItemsPanel>
        </TreeView>
    </Grid>
</Window>

C#:

 public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            TreeViewItem rootItem = new TreeViewItem() { Header = "Item Level 0" };

            for (int i = 0; i < 1000; i++)
            {
                TreeViewItem itemLevel1 = new TreeViewItem() { Header = "Item Level 1" };

                itemLevel1.Items.Add(new TreeViewItem());

                rootItem.Items.Add(itemLevel1);
            }

            treeView.Items.Add(rootItem);
        }

        private void TreeView_CleanUpVirtualizedItem(object sender, CleanUpVirtualizedItemEventArgs e)
        {

        }
    }
3个回答

7
区别在于UI虚拟化(WPF对不同控件支持开箱即用)与数据虚拟化(WPF不支持开箱即用)。
UI虚拟化只渲染所需和可见的内容;而数据虚拟化仅将可能在给定时间内需要的内容保存在内存中。
Bea有两篇关于UI虚拟化数据虚拟化的绝佳文章,介绍了它们之间的区别以及如何解决不支持数据虚拟化的限制,这似乎是您想要的。
编辑:从3.5 SP1开始,TreeView添加了对虚拟化的支持。也许删除ItemsPanel模板,只设置TreeView上的属性就足够了。

1
我明白。在我的问题中,我谈到的是UI虚拟化,这就是为什么我不理解为什么它会为这10001个项目中的每一个创建一个容器,尽管虚拟化被设置为true。 - Carlo
1
@Carlos 增加了有关删除您的ItemsPanel模板的注释; 因为我认为自3.5 SP1起,默认模板应该是VirtualizingStackPanel,所以不需要更改模板,只需像您通过TreeView一样启用属性即可。 - Aaron McIver
尝试了你在编辑中提到的方法,但仍然不起作用。我仍然会得到每个控件的一千个副本。这真的很奇怪。而且,CleanUpVirtualizedItem事件仍然没有被触发,这是另一个表明虚拟化未正常工作的线索。 - Carlo
@Carlos 有没有应用样式隐藏在某个地方?你把它缩小到只有上面的代码了吗?另一个尝试的方法是通过DataBound数据而不是逐个显式添加... - Aaron McIver
2
好的,我明白了。虚拟化只能与绑定一起使用...真是个糟糕的事情。 - Carlo
@Carlo... 瞧瞧我的最后一条评论 :) - Aaron McIver

6

好的,问题在于虚拟化只有在TreeView使用绑定时才能工作,而不是像我的示例代码一样逐个生成节点。真遗憾。


5
这不应该是一个太大的障碍,只需使用ObservableCollection将树形视图绑定,并在需要时将项目添加到集合中 - 这也是更符合WPF惯例的方式,您不希望直接手动添加项目。 - BrokenGlass
我喜欢你的想法。当我在树上向上或向下滚动时,我会知道我需要更多的项目。你认为这种方法可行吗? - Carlo

3

那不是很准确。虚拟化只能与绑定一起使用,但是在某些示例应用程序中我观察到元素的回收仅发生在树视图的根项目上。

因此,如果您只有一个具有1000个子项的根项目,则不会发生回收,因为只有1个可供回收的容器。

如果您有100个根项目,并且每个项目都有100个子项,则将获得部分回收,因为只有根100个项目将被回收,但其他9900个子项将存储在内存中。

当然,如果您的树嵌套级别大于2或3,则问题只会变得更糟。

我不知道是否有解决此问题的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接