优化WPF 4.0 TreeView滚动性能

时间:2012-10-26 15:09:13

标签: c# .net wpf wpf-4.0

我的上一个问题已经太久了,所以这里是一个新问题!

我在滚动期间遇到虚拟化WPF TreeView的性能问题。假设我的TreeViewItems下面有任意复杂的控件可能需要很长时间 - 比方说10ms - 需要测量。向下滚动TreeView会很快变得非常迟缓,因为为每个滚动单元调用布局传递。

在这种情况下,您有什么想要顺利滚动? 我尝试了两种不同的方法,但我遇到了两个问题。

第一个是缓冲最后一个MeasureOverride结果并使用它而不是再次调用MeasureOverride,除非更改大小。只要我在树中只有一个层次结构级别,它就可以正常工作。但是一旦我得到更多,一些项目不会被渲染(尽管大小被正确保留)。使缓冲区无效不会成为问题。

第二个是在滚动期间替换了TreeViewItems下的ControlTemplate,这要归功于Trigger,使用ControlTemplate可以非常快速地进行渲染。可悲的是,当我停止滚动已经附加在逻辑树中的regargind项目时,我会遇到奇怪的异常。

最后,我不在多线程环境中,因此无法使用异步绑定。 :(

这是我想改进的树的一个非常小的例子(它是实现我的第一个想法的那个),我只是在用于渲染我的Items的ContentControl的MeasureOverride中放入一个Thread.Sleep(10)。

public class Item
{
    public string Index { get; set; }
    public Item Parent { get; set; }

    public IEnumerable<Item> Items
    {
        get
        {
            if (Parent == null)
            {
                for (int i = 0; i < 10; i++)
                {
                    yield return new Item() { Parent = this, Index = this.Index + "." + i.ToString() };
                }
            }
        }
    }
}

public class HeavyControl : ContentControl
{
    protected override Size MeasureOverride(Size constraint)
    {
        Thread.Sleep(10);
        return base.MeasureOverride(constraint);
    }
}

/// <summary>
/// Logique d'interaction pour MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }

    public IEnumerable<Item> Items
    {
        get
        {
            for (int i = 0; i < 20; i++)
            {
                yield return new Item() { Index = i.ToString() };
            }
        }
    }
}

public class MyTreeView : TreeView
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new MyTreeViewItem();
    }
}

public class MyTreeViewItem : TreeViewItem
{
    static MyTreeViewItem()
    {
        MyTreeViewItem.IsExpandedProperty.OverrideMetadata(typeof(MyTreeViewItem), new FrameworkPropertyMetadata(true));
    }

    public Size LastMeasure
    {
        get { return (Size)GetValue(LastMeasureProperty); }
        set { SetValue(LastMeasureProperty, value); }
    }

    // Using a DependencyProperty as the backing store for LastMeasure.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LastMeasureProperty =
        DependencyProperty.Register("LastMeasure", typeof(Size), typeof(MyTreeViewItem), new UIPropertyMetadata(default(Size)));

    protected override Size MeasureOverride(Size constraint)
    {
        if (LastMeasure != default(Size))
        {
            if (this.VisualChildrenCount > 0)
            {
                UIElement visualChild = (UIElement)this.GetVisualChild(0);
                if (visualChild != null)
                {
                    visualChild.Measure(constraint);
                }
            }

            return LastMeasure;
        }
        else
        {
            LastMeasure = base.MeasureOverride(constraint);
            return LastMeasure;
        }
    }

    protected override DependencyObject GetContainerForItemOverride()
    {
        return new MyTreeViewItem();
    }
}

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="MainWindow" Height="350" Width="525">

<Window.Resources>
    <HierarchicalDataTemplate DataType="{x:Type local:Item}" ItemsSource="{Binding Items}">
        <local:HeavyControl>
            <TextBlock FontSize="20" FontWeight="Bold" Text="{Binding Index}" />
        </local:HeavyControl>
    </HierarchicalDataTemplate>
</Window.Resources>

<local:MyTreeView x:Name="treeView" VirtualizingStackPanel.IsVirtualizing="True" ItemsSource="{Binding Items}" />

</Window>

1 个答案:

答案 0 :(得分:2)

我会尝试修复第二种方法中的错误。

  • 为树的每个节点/项目提供唯一的密钥
  • 拥有包含每个节点/项目中的控件的字典
  • 滚动时(或者即使没有聚焦,只需在树中显示数据的文本/简单表示。
  • 一旦节点/项目被聚焦,使用唯一键从字典中用适当的控件替换简单表示。
  • 当节点/项目失去焦点时,将其恢复为简单表示,因为更改仍存储在引用控件的字典值中