2

距离我上一个问题已经太久了,所以这里有一个新问题!

我在滚动期间遇到了虚拟化 WPF TreeView 的性能问题。假设我的 TreeViewItems 下有任意复杂的控件,这些控件可能需要很长时间(比如说 10 毫秒)才能进行测量。向下滚动 TreeView 很快就会变得非常缓慢,因为每个向下滚动单元都会调用 Layout pass。

在这种情况下获得平滑滚动的想法是什么?我尝试了两种不同的方法,但我都遇到了问题。

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

第二个是在滚动期间通过触发器替换 treeViewItems 下控件的 ControlTemplate,使用 ControlTemplate 非常快速地呈现。可悲的是,当我停止滚动已附加在逻辑树中的项目时,我遇到了奇怪的异常。

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

这是我想要改进的树的一个非常小的示例(这是实现我的第一个想法的树),我只是将 Thread.Sleep(10) 放在用于呈现我的项目的 ContentControl 的 MeasureOverride 中。

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

主窗口.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>
4

1 回答 1

2

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

  • 给树的每个节点/项目一个唯一的键
  • 拥有包含每个节点/项目中的控件的字典
  • 滚动时(甚至不在焦点时,只需在树中显示数据的文本/简单表示。
  • 一旦一个节点/项目被聚焦,使用唯一键从字典中用适当的控件替换简单的表示。
  • 当我的节点/项目失去焦点时,将其恢复为简单的表示,因为更改仍存储在引用控件的字典值中
于 2012-10-26T15:18:32.347 回答