博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
控件UI性能调优 -- SizeChanged不是万能的
阅读量:6942 次
发布时间:2019-06-27

本文共 3717 字,大约阅读时间需要 12 分钟。

原文:

简介

我们在之前的““一文中曾提到XAML的布局系统 和平时使用上的一些问题(重写Measure/Arrange还是使用SizeChanged?),这篇博文就来为大家简单地描述一下XAML布局系统的行为,并且归纳几个规则。当然真正的XAML布局系统十分复杂,本文无意把情况弄得太复杂,就从一个最简单最直观的例子入手,来为大家提供一点理解XAML布局的新思路。

 

问题描述

假设我们有一个Templated Control,其XAML描述如下:

两个Border嵌套,边宽20。我们的目的就是通过代码来改变InnerBorder的大小。比如长宽都变成OuterBorder的一半大。

 

首次尝试

我们很容易就写出了这样的代码:

public sealed class CustomControl1 : Control{    public CustomControl1() {...}    private Border _border;    private Border _inner;    protected override void OnApplyTemplate()    {        base.OnApplyTemplate();        _border = GetTemplateChild("OuterBorder") as Border;        _inner = GetTemplateChild("InnerBorder") as Border;        if (_border != null && _inner != null)        {            _border.SizeChanged += (s, e) => {                _inner.Width = _border.ActualWidth / 2;                _inner.Height = _border.ActualHeight / 2;            };        }    }}

works perfectly。这一实现很好地达到了我们的需求。(而且对于这样的简单的情况设计器还是能够正常处理的)

 

对SizeChanged的概述

但是这却隐藏着问题。首先,SizeChanged事件是由一轮Measure/Arrange完成后触发的。

XAML的核心布局流程,是从根元素 即页面开始,递归向下。第一次挨个调用Measure,提供能用的大小,并确定每个子项所希望的空间大小;再来一次挨个调用Arrange,提供能用大小,按实际情况给子项分配空间(不一定能满足它们的需要)和确定位置。本例的过程中就涉及到OuterBorderInnerBorder,它们以此能根据Border类布局规则确定自己的大小,即刨去BorderThickness。

 

这之后,OuterBorderInnerBorder实际大小就确定了。如果和上次布局的结果不一样,OuterBorder就会触发SizeChanged事件(是Chang*ed*哦),改变InnerBorder设定大小。因为设定大小变化了,会引发新一轮递归Measure和Arrange。这一次之后,OuterBorder的大小不变,InnerBorder的大小变成OuterBorder的一半。之后没有事件和布局再被触发,大家相安无事。

但实际上,布局进行了两轮。如果Visual Tree很大的话,后果可想而知。

 

修改后的过程

那么,根据我们刚才介绍的过程,从Measure出发,实现如下(去掉SizeChanged的事件绑定并override MeasureOvrride方法):

public sealed class CustomControl1 : Control{    public CustomControl1() {...}    private Border _border;    private Border _inner;    protected override void OnApplyTemplate()    {        base.OnApplyTemplate();        _border = GetTemplateChild("Border") as Border;        _inner = GetTemplateChild("InnerBorder") as Border;    }    protected override Size MeasureOverride(Size availableSize)    {        // availableSize就是OuterBorder的大小        if (_inner != null)        {            _inner.Width = availableSize.Width / 2;            _inner.Height = availableSize.Height / 2;        }        return base.MeasureOverride(availableSize);    }}

设定大小后再进入真正的measure环节,一次性搞定布局。原因就在于我们在布局开始之前就搞定了Size信息,而不是在布局结束后再把它辛辛苦苦计算出来的Size踩在地上并让它重来一遍。在我们设定的需求看来,甚至无需插手Arrange流程。

当然,这免不了地要自己计算Size,可能需要手动减去BorderThickness的大小,甚至还可能要自行调用一次Measure。复杂的具体情况需要具体分析。

 

性能对比

通过调试工具,我们来对比一下两种方法的实际性能:

SizeChanged MeasureOverride

在两种实现下,分别大力地快速拖动窗口大小。。。

其中柱形图是一段时间内UI线程的响应情况,占最大比重的橙色是布局行为。下面的扇形图是选中差不多的时间段内,布局消耗的占比情况。

可见通过提供Measure策略的方式,即使是这样简单的设定,性能提升也还看得出来。

 

如果我们发扬奥卡姆剃刀的精神,不要自己写这陌生的MeasureOverride,用Grid来做如何?

OnApplyTemplateMeasureOverride都可以不要了,整个code behind十分清爽。行为看起来差不多,那么性能呢?

想必Grid作为标准控件,优化得应该很好了,但它本身就有一点复杂,和MesureOverride的实现在性能上有一点点差距。但毕竟我们这样简单的例子对于Grid太不公平了,对于更为复杂的情况,还是要使用Grid的。

 

总结

说了这么多,主要是表现一下不必要的布局对于性能的影响,以及对于这样的简单情况如何替代原有实现。

对于布局有影响的操作大致有:

  • 改变大小:设置WidthHeight、MaxHeight(如果影响到ActualHeight),或者修改MarginThickness
  • 改变内容:设置ContentContentTemplateDataTemplateTextBox.Text
  • 改变某些属性:如VisibleOrientationImage.Stretch
  • 手动调用布局方法:InvalidateMeasureUpdateLayout

如果调用了这些属性方法,就需要顾虑一下是否会造成不必要的布局了,特别是在SizeChanged这样的由布局触发的事件里。当然这也是一般论,如果控件本来就隐藏了,或者Template改变了原有外型,这些内容也自然随之变化。

P.S. RenderTransform是不造成重新布局的。

 

另外,就本文的例子来说,并不是要大家都把SizeChanged改写成MeasureOverride

MeasureOverride给了一个好处,就是第一时间获知高层布局的相关信息,也就能赶在布局前最后设置一次属性;SizeChanged能给出复杂布局计算后的最新尺寸,如果自己来计算的话没有意义。总之还是要因地制宜。

 

虽然本文的例子十分简单,可能没有多少实际意义,不过希望通过它介绍的流程,能为大家的开发提供一点新的思路。

 

参考

[1] 开源的WPF中的Border.MeasureOverride实现:

[2] WPF中的Grid.MeasureOverride实现:

[3] SizeChanged事件参考:

转载地址:http://hjinl.baihongyu.com/

你可能感兴趣的文章
Skype已死?触宝电话和微信说:“有事烧纸“
查看>>
高燕婕:解读中国“十三五”智慧医疗与健康服务业之发展
查看>>
安防在金融行业应用的未来发展趋势
查看>>
世界那么大,微信国际化
查看>>
开源SDN来势汹汹 ODL中国实战弥补人才短板
查看>>
线下渠道大热 迪信通信心爆棚要“做机”
查看>>
聚焦数字营销 看中美巨头如何玩转大数据
查看>>
汇丰银行:为什么机器学习正在加速云计算的采用
查看>>
Martin Casado预测:网络基础设施并不会消亡
查看>>
阿尔卡特朗讯企业通信发布下一代的中小企业解决方案 支持云服务
查看>>
波动之后 如何看当前光伏板块盈利与投资
查看>>
共建共享 推进大数据深度应用
查看>>
一个CIO的自白:如何推动传统公司转型上云?
查看>>
光伏跟踪系统的春天还有多远?
查看>>
三伏天衣食住行注意事项
查看>>
思博伦Tweakker赢得亚洲首个 MVNO合同
查看>>
Qt之资源系统
查看>>
RDS PostgreSQL\HDB PG 毫秒级海量时空数据透视 典型案例分享
查看>>
《数据库技术原理与应用教程》一3.5.3关系模型的数据结构、操纵和约束
查看>>
《超越LOGO设计:国际顶级平面设计师的成功法则(第2版)》—第1章无处不在的LOGO...
查看>>