2012-10-26 77 views
2

從我上一個問題開始,這已經太長了,所以這裏是一個新的!優化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> 
+0

「比方說,我有在我的TreeViewItems任意複雜的控制這可能需要很長時間 - 比方說10ms--要被測量「 - >爲什麼這些控制措施需要時間來衡量?要優化這一點並不簡單,並從根本上解決這個問題? – mathieu

+0

不幸的是,這是不可能的。這些控件已經儘可能優化,有些將繼續花費大約5到10ms來測量。 – Sisyphe

回答

2

我會嘗試在你的第二個方法來修復錯誤(或多個)。

  • 給樹保存在每個節點/項目的控制(S)的唯一密鑰
  • 有無字典的每個節點/項目
  • 雖然滾動(或者即使沒有焦點,只是有一個文本/簡單樹中數據的表示。
  • 一旦節點/項目被集中,使用唯一鍵替換簡單表示和字典中的適當控件。
  • 當我節點/項目失去焦點,將其恢復爲一個簡單的表示,因爲變化仍然存儲在字典中的值引用控制(S)
+0

看起來像個好主意!我會試試:) – Sisyphe