從我上一個問題開始,這已經太長了,所以這裏是一個新的!優化WPF 4.0 TreeView滾動性能
我在滾動期間遇到虛擬化WPF TreeView的性能問題。假設我在我的TreeViewItems下有任意的複雜控件,可能需要很長時間 - 比如10ms - 才能被測量。向下滾動TreeView可能很快變得非常滯後,因爲每個滾動單位都會調用佈局傳遞。
在這種情況下,您的想法是什麼才能順利滾動? 我嘗試了兩種不同的方法,但我遇到了兩個問題。
首先是緩衝最後一個MeasureOverride結果並使用它,而不是再次調用MeasureOverride,除非大小改變。只要我在樹中只有一個層次結構級別,它就可以工作。但只要我得到更多,一些項目不呈現(雖然大小是正確保留)。使緩衝區無效將不成問題。
第二個是在滾動期間使用ControlTemplate非常快速地渲染,在滾動期間替換TreeViewItems下控件的ControlTemplate。不幸的是,當我停止滾動已經附加在邏輯樹中的regargind項目時,我收到了奇怪的異常。
最後,我不在多線程環境中,因此將無法使用異步綁定。 :(
這是我想要改進的樹的一個非常小的例子(這是實現我的第一個想法的一個例子),我只是將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();
}
}
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>
「比方說,我有在我的TreeViewItems任意複雜的控制這可能需要很長時間 - 比方說10ms--要被測量「 - >爲什麼這些控制措施需要時間來衡量?要優化這一點並不簡單,並從根本上解決這個問題? – mathieu
不幸的是,這是不可能的。這些控件已經儘可能優化,有些將繼續花費大約5到10ms來測量。 – Sisyphe