Прошло слишком много времени с моего последнего вопроса, так что вот новый!
У меня возникают проблемы с производительностью при виртуализации WPF TreeView во время прокрутки. Допустим, у меня есть произвольные сложные элементы управления в моих TreeViewItems, измерение которых может занять много времени, скажем, 10 мс. Прокрутка TreeView вниз может быстро стать очень запаздывающей, так как проход Layout вызывается для каждой единицы прокрутки вниз.
Каковы были бы ваши идеи, чтобы получить плавную прокрутку в этом случае? Я пробовал два разных подхода, но у меня проблемы с обоими.
Первый заключался в том, чтобы буферизовать последний результат MeasureOverride и использовать его вместо повторного вызова MeasureOverride, если размер не изменился. Это работает, пока у меня есть только один уровень иерархии в дереве. Но как только получаю больше, некоторые элементы не рендерятся (хотя размер правильно зарезервирован). Инвалидация буфера не будет проблемой.
Во-вторых, заменить ControlTemplate элементов управления в treeViewItems во время прокрутки благодаря Trigger, используя очень быстро отображаемый ControlTemplate. К сожалению, я получаю странные исключения, когда перестаю прокручивать элементы, уже прикрепленные к логическому дереву.
Наконец, я не нахожусь в многопоточной среде и поэтому не смогу использовать асинхронные привязки. :(
Вот очень небольшой пример дерева, которое я хотел бы улучшить (это тот, который реализует мою первую идею).
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>