前回の質問から時間がかかりすぎたので、ここに新しい質問があります !
スクロール中に仮想化 WPF TreeView でパフォーマンスの問題が発生しています。TreeViewItems の下に任意の複雑なコントロールがあり、測定に長い時間 (10ms としましょう) がかかるとします。スクロール ユニットごとにレイアウト パスが呼び出されるため、TreeView のスクロール ダウンはすぐに非常に遅くなる可能性があります。
この場合、スムーズなスクロールを実現するためのアイデアは何ですか? 私は2つの異なるアプローチを試みましたが、両方に問題があります。
最初の 1 つは、最後の MeasureOverride の結果をバッファリングし、サイズが変更されない限り、MeasureOverride を再度呼び出す代わりにそれを使用することでした。ツリーに階層レベルが 1 つしかない限り、機能しています。しかし、さらに取得するとすぐに、一部のアイテムがレンダリングされません (ただし、サイズは正しく予約されています)。バッファの無効化は問題になりません。
2 つ目は、スクロール中に TreeViewItems の下にあるコントロールの ControlTemplate を Trigger のおかげで置き換え、ControlTemplate を使用して非常に迅速にレンダリングすることでした。悲しいことに、論理ツリーに既にアタッチされているアイテムに関してスクロールを停止すると、奇妙な例外が発生します。
最後に、私はマルチスレッド環境にいないため、非同期バインディングを使用できません。:(
これは私が改善したいツリーの非常に小さな例です (これは私の最初のアイデアを実装したものです)。アイテムをレンダリングするために使用される 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>