寻找赣州网站建设,网站上怎么做弹幕效果,如何做淘客发单网站,网站接入服务商查询简介 我们在之前的“UWP控件开发——用NuGet包装自己的控件“一文中曾提到XAML的布局系统 和平时使用上的一些问题#xff08;重写Measure/Arrange还是使用SizeChanged#xff1f;#xff09;#xff0c;这篇博文就来为大家简单地描述一下XAML布局系统的行为#xff0c;并…简介 我们在之前的“UWP控件开发——用NuGet包装自己的控件“一文中曾提到XAML的布局系统 和平时使用上的一些问题重写Measure/Arrange还是使用SizeChanged这篇博文就来为大家简单地描述一下XAML布局系统的行为并且归纳几个规则。当然真正的XAML布局系统十分复杂本文无意把情况弄得太复杂就从一个最简单最直观的例子入手来为大家提供一点理解XAML布局的新思路。 问题描述 假设我们有一个Templated Control其XAML描述如下 Style TargetTypelocal:CustomControl1Setter PropertyTemplateSetter.ValueControlTemplate TargetTypelocal:CustomControl1Border x:NameOuterBorderBorderBrushYellowBorderThickness20Border x:NameInnerBorderBorderThickness20BorderBrushRed //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提供能用大小按实际情况给子项分配空间不一定能满足它们的需要和确定位置。本例的过程中就涉及到OuterBorder和InnerBorder它们以此能根据Border类布局规则确定自己的大小即刨去BorderThickness。 这之后OuterBorder和InnerBorder的实际大小就确定了。如果和上次布局的结果不一样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来做如何 GridGrid.ColumnDefinitionsColumnDefinition Width*/ColumnDefinition Width2*/ColumnDefinition Width*//Grid.ColumnDefinitionsGrid.RowDefinitionsRowDefinition Height*/RowDefinition Height2*/RowDefinition Height*//Grid.RowDefinitionsBorder x:NameBorderBorderBrushYellowBorderThickness20Grid.RowSpan3 Grid.ColumnSpan3/Border x:NameInnerBorderBorderThickness20BorderBrushRed Grid.Column1 Grid.Row1/
/Grid OnApplyTemplate和MeasureOverride都可以不要了整个code behind十分清爽。行为看起来差不多那么性能呢 想必Grid作为标准控件优化得应该很好了但它本身就有一点复杂和MesureOverride的实现在性能上有一点点差距。但毕竟我们这样简单的例子对于Grid太不公平了对于更为复杂的情况还是要使用Grid的。 总结 说了这么多主要是表现一下不必要的布局对于性能的影响以及对于这样的简单情况如何替代原有实现。 对于布局有影响的操作大致有 改变大小设置Width、Height、MaxHeight如果影响到ActualHeight或者修改Margin、Thickness等改变内容设置Content、ContentTemplate、DataTemplate、TextBox.Text等改变某些属性如Visible、Orientation、Image.Stretch等手动调用布局方法InvalidateMeasure、UpdateLayout等如果调用了这些属性方法就需要顾虑一下是否会造成不必要的布局了特别是在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