C# 优化WPF 4.0树视图滚动性能
我已经很久没问上一个问题了,所以这里有一个新问题C# 优化WPF 4.0树视图滚动性能,c#,.net,wpf,wpf-4.0,C#,.net,Wpf,Wpf 4.0,我已经很久没问上一个问题了,所以这里有一个新问题 我在滚动过程中遇到虚拟化WPF树视图的性能问题。假设我的TreeView项下有任意复杂的控件,需要很长时间(比如说10毫秒)才能测量。向下滚动TreeView会很快变得非常滞后,因为每个向下滚动单元都会调用布局过程 在这种情况下,您有什么想法来实现平滑滚动? 我尝试了两种不同的方法,但都有问题 第一个是缓冲最后一个MeasureOverride结果,并使用它,而不是再次调用MeasureOverride,除非大小发生变化。只要我在树中只有一个层次
我在滚动过程中遇到虚拟化WPF树视图的性能问题。假设我的TreeView项下有任意复杂的控件,需要很长时间(比如说10毫秒)才能测量。向下滚动TreeView会很快变得非常滞后,因为每个向下滚动单元都会调用布局过程 在这种情况下,您有什么想法来实现平滑滚动? 我尝试了两种不同的方法,但都有问题 第一个是缓冲最后一个MeasureOverride结果,并使用它,而不是再次调用MeasureOverride,除非大小发生变化。只要我在树中只有一个层次结构级别,它就可以工作。但当我得到更多时,一些项目就不会被渲染(尽管大小已正确保留)。使缓冲区无效不是问题 第二个是在滚动过程中使用触发器替换treeViewItems下控件的ControlTemplate,使用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();
}
}
公共类项目
{
公共字符串索引{get;set;}
公共项父项{get;set;}
公共数字项目
{
得到
{
如果(父项==null)
{
对于(int i=0;i<10;i++)
{
产生返回新项(){Parent=this,Index=this.Index+“+i.ToString()};
}
}
}
}
}
公共类重控件:ContentControl
{
受保护的覆盖尺寸测量覆盖(尺寸约束)
{
睡眠(10);
返回基准。测量超越(约束);
}
}
///
///Logique d'interaction pour main window.xaml
///
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
DataContext=this;
}
公共数字项目
{
得到
{
对于(int i=0;i<20;i++)
{
产生返回新项(){Index=i.ToString()};
}
}
}
}
公共类MyTreeView:TreeView
{
受保护的覆盖依赖对象GetContainerForItemOverride()
{
返回新的MyTreeViewItem();
}
}
公共类MyTreeView项目:TreeView项目
{
静态MyTreeViewItem()
{
MyTreeViewItem.IsExpandedProperty.OverrideMetadata(typeof(MyTreeViewItem),新框架属性ymetadata(true));
}
公共尺寸
{
获取{return(Size)GetValue(lastmasureproperty);}
set{SetValue(lastmasureproperty,value);}
}
//使用DependencyProperty作为LastMeasure的备份存储。这将启用动画、样式、绑定等。。。
公共静态只读从属属性LastMeasureProperty=
DependencyProperty.Register(“LastMeasure”、typeof(大小)、typeof(MyTreeViewItem)、new UIPropertyMetadata(默认大小));
受保护的覆盖尺寸测量覆盖(尺寸约束)
{
如果(LastMeasure!=默认值(大小))
{
如果(this.VisualChildrenCount>0)
{
UIElement visualChild=(UIElement)this.GetVisualChild(0);
如果(visualChild!=null)
{
视觉儿童测量(约束);
}
}
返回测量值;
}
其他的
{
LastMeasure=基础。测量超越(约束);
返回测量值;
}
}
受保护的覆盖依赖对象GetContainerForItemOverride()
{
返回新的MyTreeViewItem();
}
}
main window.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>
我将尝试修复第二种方法中的错误
- 为树的每个节点/项指定一个唯一的键
- 拥有保存每个节点/项中的控件的字典
- 滚动时(甚至在不聚焦时),只需在树中显示数据的文本/简单表示
- 聚焦节点/项后,使用唯一键将简单表示替换为字典中的适当控件
- 当I节点/项失去焦点时,将其恢复为简单表示,因为更改仍存储在引用控件的字典值中