简体   繁体   English

在虚拟化TreeView中滚动问题

[英]Scrolling Issue in Virtualized TreeView

Today I decided to finally try virtualized TreeView. 今天我决定最终尝试虚拟化TreeView。 To do that binding is required. 要做到这一点,需要绑定。 So I decided to get 2 things test - HierarchicalDataTemplate based on types + virtualization. 所以我决定进行2项测试 - 基于类型+虚拟化的HierarchicalDataTemplate。

I created a base class for some data. 我为一些数据创建了一个基类。 Created 2 derived classes from base class. 从基类创建了2个派生类。 Made 2 HierarchicalDataTemplate (1 for each derived class) to get different formatting of nodes. 制作2个HierarchicalDataTemplate(每个派生类1个)以获得不同的节点格式。 And running population of 10k nodes of 2 types. 并运行2种类型的10k节点的人口。

Classes: 类别:

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);
    }
}

Generation of 10k 1st level nodes with some children, binding: 生成带有一些子节点的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();
    }

XAML with HierarchicalDataTemplates: 带有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>

Everything works finely: 一切都很好:

  • treeview gets virtualized (easily noticeable by memory footprint + loading times) treeview变得虚拟化(通过内存占用和加载时间很容易注意到)
  • Nodes derived from types are correctly formatted 从类型派生的节点格式正确

Yet, when scrolling to let's say header #1000 and expanding it - scroll position would jump to some other place making expanded node and its children NOT visible. 然而,当滚动让我们说标题#1000并扩展它时 - 滚动位置会跳转到其他地方,使得展开的节点及其子节点不可见。

What did I do wrong? 我做错了什么? Is there any way to fix this? 有没有什么办法解决这一问题?

Update: Removing virtualization also removes scrolling bug. 更新:删除虚拟化也会删除滚动错误。

After having many issues with Tree virtualization in C# WPF (including major issue, that only the very first level gets virtualized) - I was not able to find a proper fix. 在C#WPF中遇到树虚拟化的许多问题(包括主要问题,只有第一级被虚拟化) - 我无法找到正确的修复。 Microsoft accepted a bug report and answered, that scrolling issue will get fixed in one of future releases. 微软接受了一个错误报告并回答说,滚动问题将在未来的一个版本中修复。

As to the final solution to this for me personally - I've switched to own implementation of ListTreeView, ie using List and simulating tree. 至于我个人的最终解决方案 - 我已经切换到自己的ListTreeView实现,即使用List和模拟树。 This solved all the issues with virtualization and with scrolling behavior. 这解决了虚拟化和滚动行为的所有问题。 The only issue was - removal of many items after collapsing tree node. 唯一的问题是 - 折叠树节点后删除许多项目。 I had to implement a check if it's easier/faster to just recreate a fresh list instead of deleting items 1 by 1. 我必须实现检查是否更容易/更快地重新创建一个新的列表而不是逐个删除项目。

Try turning off (not on) Recycling 尝试关闭(不开启)回收

VirtualizingStackPanel.VirtualizationMode="Standard"

The optimization article refers to turning it on 优化文章指的是打开它

How to: Improve the Performance of a TreeView 如何:提高TreeView的性能

I'd appreciate if some1 could just test this in their environment and confirm the issue. 如果some1可以在他们的环境中测试这个并确认问题,我会很感激。 Also, playing around a little bit found another strange behavior: 此外,玩一点点发现另一个奇怪的行为:

  • changed mode to Standart 将模式更改为Standart
  • implemented IsExpanded two-way binding (not sure if this is required at all) 实现IsExpanded双向绑定(不确定是否需要)

Next run the program: 接下来运行程序:

  • Expand header with some big number, like 1000 - scroller jumps to some other place 使用一些大数字展开标题,例如1000 - 滚动条跳转到其他地方

Rerun the program (for purity of experiment) 重新运行程序(实验纯度)

  • Expand one of first headers, like #2. 展开第一个标题之一,如#2。
  • Expand header with some big number, like 1000 - scroller stays at proper location... 使用一些大数字展开标题,例如1000 - 滚动条停留在适当的位置...

Expanding one of first nodes look to be a workaround. 扩展第一个节点之一看起来是一种解决方法。

I found a solution on MSDN. 我在MSDN上找到了一个解决方案。 Apparently it has something to do with the default TreeView template. 显然它与默认的TreeView模板有关。 The following fixes the scroll bar flickering and stops random nodes from expanding when scrolling rapidly. 以下修复了滚动条闪烁并在快速滚动时阻止随机节点扩展。

<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>

I tried rapidly scrolling and didn't seem to notice an issue anymore. 我试着快速滚动,似乎不再注意到问题了。 The awesome thing is you don't even have to do away with your virtualization. 令人敬畏的是,您甚至不必废除虚拟化。

As per https://msdn.microsoft.com/en-us/library/system.windows.controls.scrollunit(v=vs.110).aspx (ScrollUnit Enumeration), there are two possible values. 根据https://msdn.microsoft.com/en-us/library/system.windows.controls.scrollunit(v=vs.110).aspx(ScrollUnit枚举),有两个可能的值。 The default seems to be 'Pixel', which causes the scrolling issues. 默认似乎是'Pixel',这会导致滚动问题。 Switching to 'Item' as shown below resolves the issue: 如下所示切换到“项目”可解决此问题:

VirtualizingStackPanel.ScrollUnit="Item" VirtualizingStackPanel.ScrollUnit = “项目”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM