9

今天我终于决定尝试虚拟化 TreeView。要做到这一点,绑定是必需的。所以我决定做两件事测试 - 基于类型 + 虚拟化的 HierarchicalDataTemplate。

我为一些数据创建了一个基类。从基类创建了 2 个派生类。制作了 2 个 HierarchicalDataTemplate(每个派生类 1 个)以获得不同格式的节点。并运行 2 种类型的 10k 个节点。

课程:

public class ListItem_Generic
{
    public string Name { get; protected set; }
    public ListItem_Generic(string Name = "") { this.Name = Name; }
}

public class ListItem_Single : ListItem_Generic
{
    public ListItem_Single(string Name = "") : base(Name) { }
}

public class ListItem_Multi : ListItem_Generic
{
    public List<ListItem_Generic> Items { get; protected set; }
    public ListItem_Multi(string Name = "", List<ListItem_Generic> Items = null)
        : base(Name)
    {
        if (Items == null) 
            this.Items = new List<ListItem_Generic>(); 
        else 
            this.Items = new List<ListItem_Generic>(Items);
    }
}

生成带有一些子节点的 10k 一级节点,绑定:

    public MainWindow()
    {
        InitializeComponent();

        // Create a list of sample items and populate them
        var lst = new List<ListItem_Generic>();

        int MaxHeaders = 10000;
        var rnd = new Random();
        // Now generate 10 000 records. First select random amount of headers
        int HeadersCount = rnd.Next(MaxHeaders);

        for (int i = 0; i < HeadersCount; i++)
        {
            var Childrencount = rnd.Next(100);
            var children = new List<ListItem_Generic>();
            for (int j = 0; j < Childrencount; j++)
                children.Add(new ListItem_Single("Child #"+j+" of parent #"+i));
            lst.Add(new ListItem_Multi("Header #" + i + " (" + Childrencount + ")", children));
        }
        for (int i = 0; i < MaxHeaders - HeadersCount; i++)
            lst.Add(new ListItem_Single("Line #" + i));

        // Bind lstView to lst
        lstView.ItemsSource = lst;
        lstView.UpdateLayout();
    }

带有 HierarchicalDataTemplates 的 XAML:

<Window x:Class="Test_DataTemplates.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:loc="clr-namespace:Test_DataTemplates"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TreeView Name="lstView" VirtualizingPanel.IsVirtualizing="True">
            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type loc:ListItem_Multi}" ItemsSource="{Binding Path=Items}">
                    <Border Background="RosyBrown">
                        <TextBlock Text="{Binding Path=Name}" Foreground="White" FontWeight="Bold"/>
                    </Border>
                </HierarchicalDataTemplate>
                <HierarchicalDataTemplate DataType="{x:Type loc:ListItem_Single}">
                    <TextBlock Text="{Binding Path=Name}"/>
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
    </Grid>
</Window>

一切正常:

  • 树视图被虚拟化(通过内存占用+加载时间很容易注意到)
  • 从类型派生的节点格式正确

然而,当滚动到标题 #1000 并展开它时 - 滚动位置会跳转到其他地方,使展开的节点及其子节点不可见。

我做错了什么?有没有什么办法解决这一问题?

更新:移除虚拟化也移除了滚动错误。

4

5 回答 5

1

在 C# WPF 中的树虚拟化存在许多问题(包括主要问题,即只有第一级被虚拟化)之后 - 我无法找到合适的修复程序。微软接受了一份错误报告并回答说,滚动问题将在未来的某个版本中得到修复。

至于我个人的最终解决方案 - 我已经切换到自己的 ListTreeView 实现,即使用 List 和模拟树。这解决了虚拟化和滚动行为的所有问题。唯一的问题是 - 折叠树节点后删除许多项目。我必须检查是否更容易/更快地重新创建一个新列表而不是逐个删除项目。

于 2014-03-18T15:51:37.607 回答
0

尝试关闭(不打开)回收

VirtualizingStackPanel.VirtualizationMode="Standard"

优化文章是指开启它

如何:提高 TreeView 的性能

于 2013-04-12T14:22:22.817 回答
0

如果 some1 可以在他们的环境中测试并确认问题,我将不胜感激。另外,玩了一下发现了另一种奇怪的行为:

  • 将模式更改为标准
  • 实现了 IsExpanded 双向绑定(不确定这是否需要)

接下来运行程序:

  • 用一些大数字展开标题,比如 1000 - 滚动条跳转到其他地方

重新运行程序(为了实验的纯度)

  • 展开第一个标题,如#2。
  • 用一些大数字展开标题,比如 1000 - 滚动条保持在正确的位置......

扩展第一个节点看起来是一种解决方法。

于 2013-04-16T13:20:48.473 回答
0

我在 MSDN 上找到了解决方案。显然它与默认的 TreeView 模板有关。以下修复了滚动条闪烁并在快速滚动时阻止随机节点扩展。

<Style x:Key="{x:Type TreeView}" TargetType="{x:Type TreeView}">
    <Setter Property="TreeView.Background" Value="Transparent"/>
    <Setter Property="VirtualizingStackPanel.IsVirtualizing" Value="True"/>
    <Setter Property="VirtualizingStackPanel.VirtualizationMode" Value="Recycling"/>
    <Setter Property="TreeView.SnapsToDevicePixels" Value="True" />
    <Setter Property="TreeView.OverridesDefaultStyle" Value="True" />
    <Setter Property="ItemsControl.ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="TreeView.Template">
        <Setter.Value>
            <ControlTemplate TargetType="TreeView">
                <ScrollViewer Focusable="False" CanContentScroll="True" Padding="4">
                    <ItemsPresenter HorizontalAlignment="Stretch"/>
                </ScrollViewer>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我尝试快速滚动,似乎没有再注意到问题。令人敬畏的是,您甚至不必取消虚拟化。

于 2016-04-21T02:40:48.570 回答
0

根据https://msdn.microsoft.com/en-us/library/system.windows.controls.scrollunit(v=vs.110).aspx (ScrollUnit Enumeration),有两个可能的值。默认值似乎是“像素”,这会导致滚动问题。如下所示切换到“项目”可解决问题:

VirtualizingStackPanel.ScrollUnit="Item"

于 2016-12-11T10:37:55.987 回答