距离我上一个问题已经太久了,所以这里有一个新问题!
我在滚动期间遇到了虚拟化 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>