2

前回の質問から時間がかかりすぎたので、ここに新しい質問があります !

スクロール中に仮想化 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>
4

1 に答える 1

2

私はあなたの2番目のアプローチでエラーを修正しようとします。

  • ツリーの各ノード/アイテムに一意のキーを与える
  • 各ノード/アイテムのコントロールを保持する辞書を用意する
  • スクロールしている間(またはフォーカスが合っていないときでも、ツリー内のデータのテキスト/単純な表現を持っているだけです。
  • ノード/アイテムがフォーカスされたら、一意のキーを使用して、単純な表現を辞書からの適切なコントロールに置き換えます。
  • ノード/アイテムがフォーカスを失った場合、変更はコントロールを参照するディクショナリの値にまだ保存されているため、単純な表現に戻します。
于 2012-10-26T15:18:32.347 に答える