TreeView无法执行UI虚拟化。

4
我一直在研究WPF的TreeView控件中的UI虚拟化功能,据我所知,这个功能从.NET 3.5 SP1开始就可用。
我做了一个简单的项目来确保UI虚拟化被正确执行,发现它根本不起作用-所有项目都被检索出来,而不仅仅是当前显示在屏幕上的项目。
我的XAML代码如下:
<TreeView x:Name="myTree" Height="150" ItemsSource="{Binding Items}"
          VirtualizingStackPanel.IsVirtualizing="True"
          VirtualizingStackPanel.VirtualizationMode="Standard"
          ScrollViewer.IsDeferredScrollingEnabled="True" />

我的代码后台

    public IEnumerable Items { get; set; }

    public MainWindow()
    {
        Items = GenerateList();
        this.DataContext = this;

        InitializeComponent();
    }

    private IEnumerable GenerateList()
    {
        MyList list = new MyList();

        for (int i = 0; i < 1000; i++)
        {
            list.Add("Item " + i);
        }

        return list;
    }

请注意,MyList是我自己实现的IList,它持有一个ArrayList,仅转发对持有的ArrayList的调用,并将调用的方法/属性写入控制台。例如:
public object this[int index]
{
    get
    {
        Debug.WriteLine(string.Format("get[{0}]", index));
        return _list[index];
    }
    set
    {
        Debug.WriteLine(string.Format("set[{0}]", index));
        _list[index] = value;
    }
}

如果我用ListBox替换TreeView,UI虚拟化就像预期的那样工作 - 即只请求大约20个项目而不是全部1000个。

我做错了什么吗?

编辑

我也尝试将默认的ItemsPanel替换为建议的VirtualizingStackPanel,但结果相同。

1个回答

2
默认的TreeView ItemsPanelTemplateStackPanel 而不是 VirtualizingStackPanel,这就是为什么你看不到虚拟化。而对于ListBox,默认的ItemsPanelTemplate是VirtualizingStackPanel,因此在ListBox中设置VirtualizingStackPanel.IsVirtualizing="True"可以起作用。
要启用树视图上的虚拟化,除了设置属性VirtualizingStackPanel.IsVirtualizing="True"外,还需要像这样重写其默认的itemsPanelTemplate:
<TreeView x:Name="myTree" Height="150" ItemsSource="{Binding Items}"
          VirtualizingStackPanel.IsVirtualizing="True"
          VirtualizingStackPanel.VirtualizationMode="Standard"
          VirtualizingStackPanel.CleanUpVirtualizedItem="myTree_CleanUpVirtualizedItem"
          ScrollViewer.IsDeferredScrollingEnabled="True">
    <TreeView.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel IsItemsHost="True" />                   
        </ItemsPanelTemplate>
    </TreeView.ItemsPanel>
</TreeView>

我已经尝试过完全相同的方法(唯一的区别是我没有设置IsItemsHost="True",但我认为在这种情况下没有任何影响 - 如果我错了请纠正我)。抱歉我没有提到它。 - Adi Lester
你是如何达到所有1000个项目都被获取且虚拟化不起作用的点的?我尝试将事件“VirtualizingStackPanel.CleanUpVirtualizedItem”挂钩到treeView上,当任何大于20的项进入视图时,它会立即被调用。通过挂钩该事件来检查自己,你会发现虚拟化正在工作。已更新答案。 :) - Rohit Vats
您不需要覆盖ItemsPanelTemplate,只需设置VirtualizingStackPanel.IsVirtualizing即可。 - H.B.
@RV1987 日志记录代码是我在问题中提供的那个。对于我来说,索引器属性确实被调用了;我不确定为什么你没有。我正在尝试实现类似于这个的分层控件,并希望确保从ItemsSource获取项目的行为对于ListBoxTreeView是相同的。出于某种奇怪的原因,看起来并不是这样。然而,我现在认为我明白了,这种行为与UI虚拟化无关,而与ItemsControls有关。 - Adi Lester
为了澄清,我所说的行为是文章中提到的行为:“当ItemsControl绑定到IList实现而不是IEnumerable实现时,它不会枚举整个列表,而只访问需要显示的项。” 我可能会为此开一个新的、更精细的问题。 - Adi Lester
显示剩余3条评论

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