控件UI性能调优 -- SizeChanged不是万能的

简介

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

 

问题描述

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

<Style TargetType="local:CustomControl1"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="local:CustomControl1"><Border x:Name="OuterBorder"BorderBrush="Yellow"BorderThickness="20"><Border x:Name="InnerBorder"BorderThickness="20"BorderBrush="Red" /></Border></ControlTemplate></Setter.Value></Setter>
</Style>

两个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。复杂的具体情况需要具体分析。

 

性能对比

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

SizeChangedMeasureOverride

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

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

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

 

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

<Grid><Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="2*"/><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition Height="*"/><RowDefinition Height="2*"/><RowDefinition Height="*"/></Grid.RowDefinitions><Border x:Name="Border"BorderBrush="Yellow"BorderThickness="20"Grid.RowSpan="3" Grid.ColumnSpan="3"/><Border x:Name="InnerBorder"BorderThickness="20"BorderBrush="Red" Grid.Column="1" Grid.Row="1"/>
</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实现:http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Border.cs,00c166b0e025bc8d

[2] WPF中的Grid.MeasureOverride实现:http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Grid.cs,f9ce1d6be154348a

[3] SizeChanged事件参考:https://msdn.microsoft.com/en-us/library/windows/apps/windows.ui.xaml.frameworkelement.sizechanged

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/543530.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android 跨进程多实例播放demo

2019独角兽企业重金招聘Python工程师标准>>> 客户端进程需要实现&#xff0c;其中notify方法需要service 跨进程调用&#xff0c;通知客户端播放消息 IMediaPlayerClient.aidl package com.example.demo; import com.example.demo.ParcelableParcel; interface IM…

java 获取service_Java service层获取HttpServletRequest工具类的方法

Java service层获取HttpServletRequest工具类的方法大家都知道 能在Controller/action层获取HttpServletRequest&#xff0c;但是这里给大家备份的是从代码内部service层获取HttpServletRequest工具类。具体如下&#xff1a;package com.base.common.sessionutils;import javax…

Linux使用jstat命令查看jvm的GC情况

2019独角兽企业重金招聘Python工程师标准>>> 命令格式 jstat命令命令格式&#xff1a; jstat [Options] vmid [interval] [count] 参数说明&#xff1a; Options&#xff0c;选项&#xff0c;我们一般使用 -gcutil 查看gc情况 vmid&#xff0c;VM的进程号&#x…

python写一段脚本代码自动完成输入(目录下的所有)文件的数据替换(修改数据和替换数据都是输入的)【转】...

转自&#xff1a;http://blog.csdn.net/lixiaojie1012/article/details/23628129 初次尝试python语言&#xff0c;感觉用着真舒服&#xff0c;简单明了&#xff0c;库函数一调用就OK了[python] view plain copy 在CODE上查看代码片派生到我的代码片 import sys,os,os.path de…

java混合分页_坑,MySQL中 order by 与 limit 混用,分页会出现问题!

在Mysql中我们常常用order by来进行排序&#xff0c;使用limit来进行分页&#xff0c;当需要先排序后分页时我们往往使用类似的写法select * from 表名 order by 排序字段 limt M,N。但是这种写法却隐藏着较深的使用陷阱。在排序字段有数据重复的情况下&#xff0c;会很容易出现…

OSG开发概览

1 OSG基础知识 OSG是Open Scene Graphic 的缩写&#xff0c;OSG于1997年诞生于以为滑翔机爱好者之手&#xff0c;Don burns 为了对滑翔机的飞行进行模拟&#xff0c;对openGL的库进行了封装&#xff0c;osg的雏形就这样诞生了&#xff0c;1998年Don burns 遇到了同样喜欢滑翔…

hbuilder php xdebug,Hbuilder使用xdebug配置php断点调试

2019独角兽企业重金招聘Python工程师标准>>>##1. 背景不得不说Hbuilder是免费的前端开发工具中比较好用的&#xff0c;而且配合aptana开发php也马马虎虎(毕竟写前端的时候多些)。本人原是搞java的&#xff0c;后来打算用php做些个人项目(因为服务器成本低)&#xff0c…

mx播放器有没有投屏功能_无线投屏、即插即用,投影仪其实可以更智能:明基 E580T...

无论是简单的办公室会议还是小型的线下活动&#xff0c;投影仪都是必不可少的利器&#xff1a;既能有不错的显示效果&#xff0c;也不用受屏幕尺寸的约束。尽管越来越多的智能电视可供电脑连接&#xff0c;但真正派上用场的时候&#xff0c;拎箱就走的投影仪显然是最佳选择。很…

特殊矩阵-对角矩阵

2019独角兽企业重金招聘Python工程师标准>>> 挖坑。 转载于:https://my.oschina.net/aslanjia/blog/651503

vs运行时候冒了这个错:无法启动IIS Express Web 服务器~Win10

后期会在博客首发更新&#xff1a;http://dnt.dkill.net 异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 网上的方法多种&#xff0c;有让安装扩展插件的&#xff0c;有让在程序里面添加Http系列的组件&#xff0c;有其它其它的&#xff0c;可是。。。…

matlab脚本 定时停止_一触即发是什么软件?它是手机上极其强大的一款脚本辅助精灵...

一触即发app是手机上极其强大的一款免root的脚本辅助软件&#xff0c;堪称一键秒杀脚本精灵&#xff0c;除自动化、运行、管理外&#xff0c;还提供了脚本制作文字教程/视频教程&#xff0c;学习社区和图色处理、定时执行、王者荣耀、变量等命令参数和多点找色、多点找图、京东…

笔记本电脑处理器_高通提示低成本5G芯片更强大的笔记本电脑处理器

高通公司总裁克里斯蒂亚诺阿蒙(Cristiano Amon)在今天的柏林IFA贸易展览会的虚拟主题演讲中说&#xff0c;我们将很快看到更便宜的5G手机&#xff0c;以及更多具有更好应用兼容性的Windows-on-Snapdragon笔记本电脑。欧洲主要的电子产品展览会IFA今年被分成​​几个小组活动&am…

为什么6lowpan 要有四个地址_大型监控网络系统如何规划ip地址

本文转自网络对于监控项目来说&#xff0c;很多故障的原因都是跟ip地址设置不当有关&#xff0c;如ip冲突&#xff0c;或者有几路监控图像没有显示等&#xff0c;都是跟ip有一定关联&#xff0c;合理的分配ip地址十分重要。一、为什么要合理分配IP对于小型监控项目来说&#xf…

JAVA中的命令模式实例教程

原文链接 作者&#xff1a;Pankaj Kumar 译者&#xff1a;f0tlo <1357654289qq.com> 命令模式是一种行为模式&#xff0c;因此&#xff0c;它处理的是对象的行为。命令模式为系统中不同的对象提供中性化的交流媒介。根据GoF的定义&#xff0c;命令模式是&#xff1a; 通…

bugku 管理员系统 后台代码_不会吧,这也行?iOS后台锁屏监听摇一摇

[toc] 背景介绍 一般情况下&#xff0c;出于省电、权限、合理性等因素考虑&#xff0c;给人的感觉是很多奇怪的需求安卓可以实现&#xff0c;但是iOS就无法实现&#xff01;今天要介绍的需求也有这种感觉&#xff0c;就是“当 APP 处于后台或锁屏状态时&#xff0c;依旧可以监听…

windows php5.3升级,Windows10系统将PHPNOW升级PHP版本为5.3.5

Windows 10发布很久了&#xff0c;现在大多数人都在使用&#xff0c;在WIN10中使用PHPNOW&#xff0c;觉得自带的PHP版本有点低&#xff0c;所以就想升级下&#xff0c;在网上搜索了一些方法&#xff0c;然后结合自己的实际操作&#xff0c;在这里分享下。1、首先要下载PHP5.3.…

igs无法分配驱动器映射表_硬盘无法使用,用DiskPart进行分区和格式化,非常简单...

如果计算机硬盘出现问题&#xff0c;有时不是硬件上出现了错误&#xff0c;很可能只是逻辑上出现了问题&#xff0c;这时就可以使用DiskPart来快速解决。在Windows 10上&#xff0c;当外部存储(例如U盘、可移动硬盘或SD卡)由于数据损坏或其他问题而停止工作时&#xff0c;可以使…

硬件nat关闭还是开启_超能课堂(173):AfterBurner不止超频,还是绝佳的游戏伴侣...

微星AfterBurner软件可以说是一个相当好用的显卡工具&#xff0c;它好用的超频功能估计都不用我多说了吧&#xff1f;微星并没有把这款软件限制在自己品牌的显卡能用&#xff0c;各个品牌的显卡都能用&#xff0c;无论A卡还是N卡都可以用AfterBurner来超频&#xff0c;软件在超…

mx350显卡天梯图_CPU天梯图与显卡天梯图2020年最新版

最新CPU天梯图较之以往没有太大的变化&#xff0c;前十位置还是那几款。但是继阿里之后&#xff0c;腾讯也开始准备自己制作芯片了&#xff0c;毕竟自研AI芯片的诱惑还是很大的&#xff0c;2020年有不少的厂商都推出了新鲜美味的显卡&#xff0c;有些是老卡翻新做性价比。有的则…

matlab写字,Matlab实现鼠标写字代码

类型&#xff1a;编程工具大小&#xff1a;1.5M语言&#xff1a;中文 评分&#xff1a;1.2标签&#xff1a;立即下载最早的程序&#xff0c;实在忘了从哪里下载的了。能够实现鼠标的手写输入&#xff0c;但是一些不连续的点。tmouse.mfunction tmouse(action)% TMOUSE 本例展示…