本文内容
WPF 应用程序的设计可能会在计算布局和验证对象引用时产生不必要的开销,从而影响性能。 对象的构造会影响应用程序的性能特征,在运行时更是如此。本主题提供这些方面的性能改进建议。
Layout
"布局过程"一词描述了测量和排列 Panel(派生对象的子级集合)成员的过程,然后在屏幕中绘制它们。 布局处理过程是一个数学密集型过程,即:集合中的子级数目越多,所需的计算量就越大。 例如,每当集合中的子 UIElement 对象改变其位置时,它有可能触发布局系统的一个新的过程。 由于对象特征与布局行为之间的关系非常紧密,因此有必要了解可以调用布局系统的事件类型。 应用程序将尽可能减少不必要的布局处理过程调用,从而改善性能。
布局系统对集合中的每个子成员都完成两个处理过程:测量处理过程和排列处理过程。 每个子对象提供其自身对 Measure 和 Arrange 方法的重写实现,从而提供其自身特定的布局行为。 简单地说,布局是一个递归系统,实现在屏幕上对元素进行大小调整、定位和绘制。
-
子 UIElement 对象通过首先测量其核心属性来开始布局过程。
-
与大小(如 Width、Height 和 Margin)相关的对象的 FrameworkElement 属性将进行计算。
-
Panel(应用特定逻辑),例如 DockPanel 的 Dock 属性,或 StackPanel 的 Orientation 属性。
-
在测量所有的子对象后,将排列或定位内容。
-
将子对象集合绘制到屏幕上。
如果发生下列任一操作,将再次调用布局处理过程:
-
向集合中添加了一个子对象。
-
LayoutTransform 将应用于子对象。
-
UpdateLayout 方法用于调用子对象。
-
使用影响测量或排列过程的元数据进行标记的依赖属性的值发生更改时。
1、尽可能使用最高效的面板
布局过程的复杂性直接取决于 Panel(使用的派生元素)的布局行为。 例如,Grid 或 StackPanel 控件提供比 Canvas 控件更多的功能。 功能大幅度改进的代价是性能成本的大幅度提高。 但是,如果不需要 Grid 控件提供的功能,则应使用成本较低的其他控件,如 Canvas 或自定义面板。
2、更新而不替换 RenderTransform
可以更新 Transform,而不是用 RenderTransform 属性的值加以替代。 在涉及动画的方案中,尤其是这样。 通过更新现有的 Transform,可以避免启动不必要的布局计算。
3、从上到下生成树
在逻辑树中添加或删除节点时,会在该节点的父级及其所有子级上引起属性失效。 因此,应始终遵循从上到下的构造模式,以避免由于在经过验证的节点中出现不必要的失效而付出代价。 下表显示了自上而下构建树与自下而上构建树的执行速度差异,其中树有 150 级深,并且每一级都有单一的 TextBlock 和 DockPanel。
Action | 构建树(以毫秒为单位) | 呈现 - 包括生成树(以毫秒为单位) |
---|---|---|
从下到上 | 366 | 454 |
自上而下 | 11 | 96 |
以下代码示例演示如何从上到下创建树。
private void OnBuildTreeTopDown(object sender, RoutedEventArgs e)
{
TextBlock textBlock = new TextBlock();
textBlock.Text = "Default";
DockPanel parentPanel = new DockPanel();
DockPanel childPanel;
myCanvas.Children.Add(parentPanel);
myCanvas.Children.Add(textBlock);
for (int i = 0; i < 150; i++)
{
textBlock = new TextBlock();
textBlock.Text = "Default";
parentPanel.Children.Add(textBlock);
childPanel = new DockPanel();
parentPanel.Children.Add(childPanel);
parentPanel = childPanel;
}
}