版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Layout 系统概述 Measure前言在WPF/Silverlight当中,如果已经存在的Element无法满足你特殊的需求,你可能想自定义Element,那么就有可能会面临重写MeasureOverride和ArrangeOverride两个方法,而这两个方法是WPF/SL的Layout系统提供给用户的自定义接口,因此,理解Layout系统的工作机制,对自定义Element是非常有必要的。那么,究竟WPF/SL的Layout系统是怎么工作的呢?接下来,我简单的描述一下,然后,在后面的章节具体分析。简单来说,WPF的Layout系统是一个递归系统,他有两个子过程,总是以调用父元素的Meas
2、ure方法开始,以调用Ararnge方法结束,而进入每个子过程之后,父元素又会调用孩子元素的Measure,完成后,又调用孩子元素的Arrange方法,这样一直递归下去。而对两个子过程的一次调用,可以看作是一次会话,可以理解为下图所示:这个会话可以用下面一段话描述:子过程1:父根据自己的策略给孩子一个availableSize,并发起对话,通过调用孩子的Measure(availableSize)方法,询问孩子:你想要多大的空间显示自己?孩子接到询问后,根据父给的availableSize以及自己的一些限制,比如Margin,Width,等等,孩子回答:我想要XXX大小的空间。父拿到孩子给的期
3、望的空间大小后,根据自己的策略开始真正给孩子分配空间,就进入第二个子过程。子过程2:父拿到孩子的期望空间后,再根据自己的情况,决定给孩子分配finalRect大小的矩形区域,然后他发起对话,调用孩子的Arrange(finalRect)给孩子说:我给你了finalRect这么大的空间。孩子拿到这个大小后,会去布置它的内容,并且布置完成后,会告诉父:其实我用了XXX大小的空间来绘制我自己的内容。父知道后,什么也没说,还是按照分配给他的finalRect去安置孩子,如果孩子最终绘制的区域大于这个区域,就被父裁剪了。Layout过程完成。通过上面两个子过程的理解,或多或少对WPF的Layout系统有
4、个初步的了解,接下来的章节,我具体描述Measure过程和Arrange过程具体做了哪些事情,帮助你跟深入的理解Layout系统。预设条件通过下面的一个预设场景,我们来展开Layout系统的讲解。假定:我们需要自定义一个Panel,类型为*MyPanel*,MyPanel的父为*MyPanelParent*,也是一个Panel;MyPanel的孩子为*MyPanelChild*,也是一个Panel。切入点1:重写MyPanelParent的MeasureOverride()和ArrangeOverride(),研究父如何影响孩子MyPanel的Layout;切入点2:重写MyPanel.Mea
5、sureOverride()和ArrangeOverride方法,研究自身有哪些属性影响MyPanel的Layout,以及重写这两个方法时应该注意的点;注意:后面的研究,我只基于Element的Width,也就是水平方向的维度,所有的数据都是只设置水平方向的,垂直方向设置的跟水平方向一致,但不做描述。Measure过程概述1.普通基类属性对Measure过程的影响请看下面的一些设置:Window x:Class=WpfApplication1.MainWindowxmlns=xmlns:x=Title=MainWindowHeight=522Width=594Loaded=Window_Loa
6、dedxmlns:my=clr-namespace:WpfApplication1Canvas my:MyPanelParent x:Name=myPanelParent1Height=400Width=400Background=GreenCanvas.Left=10Canvas.Top=10my:MyPanel Margin=10x:Name=myPanel1Background=RedMinWidth=150Width=200MaxWidth=250/my:MyPanel Margin=10x:Name=myPanel2Background=RedMinWidth=150Width=20
7、0MaxWidth=250/my:MyPanelParent/Canvas/Window public class MyPanelParent:Panelprotected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)foreach(UIElement item in this.InternalChildren)item.Measure(new Size(120,120);/这里是入口return availableSize;protected override System.Wi
8、ndows.Size ArrangeOverride(System.Windows.Size finalSize)double x=0;foreach(UIElement item in this.InternalChildren)item.Arrange(new Rect(x,0,item.DesiredSize.Width,item.DesiredSize.Height);x+=item.DesiredSize.Width;return finalSize;public class MyPanel:Panelprotected override System.Windows.Size Me
9、asureOverride(System.Windows.Size availableSize)foreach(UIElement item in this.InternalChildren)item.Measure(availableSize);return new Size(50,50);/MyPanel返回它期望的大小protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)double xCordinate=0;foreach(UIElement item in this.I
10、nternalChildren)item.Arrange(new Rect(new Point(xCordinate,0),item.DesiredSize);xCordinate+=item.DesiredSize.Width;return finalSize;在上面的设置之后,应用程序运行起来之后,Window的表现为:分析一下设置:MyPanel1.Width=200,MyPanel1.MinWidth=150,MyPanel1.MaxWidth=250,MyPanel1.Margin=Thickness(10)MyPanel1.Measure()传入的参数为120*120,MyPane
11、l1.MeasureOverride返回的参数为50*50分析一下结果:MyPanel1实际的画出来的大小(红色部分)是100*50从结果可以看出,红色的部分受多个因素的影响,有人要问,我已经设置了MyPanel.Width=200,可是怎么画出来的Width却是100;MyPanel.Height没设置,可是画出来的却是50,为什么不是其他值。接下来我通过Measure的流程图说明一下这个结果是怎么来的:看了上图,有些人可能会看出一些端倪,也可能还不是很清晰,我按照自己的理解总结一下Measure过程究竟想干什么?1.第一点很清晰,MyPanelParent调用MyPanel.Measure
12、的过程是想得到MyPanel.DesiredSize,MyPanelParent需要在Arrange孩子MyPanel时,参考孩子的DesiredSize,决定将孩子MyPanel安置多大的空间。2.MyPanel.DesiredSize是包含Margin以及内容的大小空间3.MyPanel.MeasureOverride传入的参数constrainedSize,是基类的实现刨去Margin的大小,然后按照MyPanel对MinWidth,MaxWidth,Width的设置计算的一个MyPanel想要的值,我们自定义时在MeasureOverride当中不需要关心自己的Margin,以及其他基
13、类上影响Layout的属性,只要考虑在给定参数的范围类安排自己的内容区域;MyPanel.MinWidth,Width,MaxWidth的设定都是针对内容区域的,不含Margin部分4.如果不设定Width,那么可以在MeasureOverride返回的时候返回一个期望的内容区域大小,它会被MinWidth和MaxWidth再调整一下,调整后,还有待于MyPanelParent的衡量(旁白:别瞎折腾,也别玩Layout系统,都设置MinWidth,MaxWidth,就乖乖的呆在这个范围内。)5.不论MyPanel怎么设置自己的Width,MinWidth,MaxWidth,以及在Measure
14、Override返回一个大小,来表明自己期望多大的空间显示自己的内容,但这些都仅仅是期望的,期望是美好的,现实是残酷的,这一切还必须限定在MyPanel.Measure开始时传入的参数availableSize刨去MyPanel.Margin后的范围内,小于这个范围就满足,大于这个范围就被裁断。(可怜呀,总是受制于父)6.影响Measure过程的参数和属性存在一个优先级的,大概如下所示:Measure方法参数availableSize MinWidth,Width,MaxWidth MeasureOverride返回值2.Transform对Measure过程的影响通过上面的过程,我们已经大概
15、了解了Measure过程的工作方式,以及各个属性是如何影响的。但是还有一个属性我们没有提及,但它对Measure的过程也影响甚大,这就是LayoutTransform。通过下面的两段分析,你会看到这个属性的具体表现。设置1:Window x:Class=WpfApplication1.MainWindowxmlns=xmlns:x=Title=MainWindowHeight=522Width=594Loaded=Window_Loadedxmlns:my=clr-namespace:WpfApplication1Canvas my:MyPanelParent x:Name=myPanelPa
16、rent1Height=400Width=400Background=LimeCanvas.Left=10Canvas.Top=10my:MyPanel Margin=10x:Name=myPanel1Background=RedWidth=200my:MyPanel.LayoutTransform RotateTransform Angle=90/my:MyPanel.LayoutTransform/my:MyPanel my:MyPanel Margin=10x:Name=myPanel2Background=RedMinWidth=150MaxWidth=250/my:MyPanelPa
17、rent/Canvas/Window public class MyPanelParent:Panelprotected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)foreach(UIElement item in this.InternalChildren)item.Measure(new Size(1000,800);return availableSize;protected override System.Windows.Size ArrangeOverride(Syst
18、em.Windows.Size finalSize)double x=0;foreach(UIElement item in this.InternalChildren)item.Arrange(new Rect(x,0,item.DesiredSize.Width,item.DesiredSize.Height);x+=item.DesiredSize.Width;return finalSize;public class MyPanel:Panelprotected override System.Windows.Size MeasureOverride(System.Windows.Si
19、ze availableSize)foreach(UIElement item in this.InternalChildren)item.Measure(availableSize);return new Size(80,50);protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)double xCordinate=0;foreach(UIElement item in this.InternalChildren)item.Arrange(new Rect(new Point
20、(xCordinate,0),item.DesiredSize);xCordinate+=item.DesiredSize.Width;return finalSize;运行的表现为:分析一下设置:MyPanel1.LayoutTransform=new RotateTransform(90)/旋转了90度MyPanel1.Width=200 MyPanel1.Margin=Thickness(10)MyPanel1.Measure()传入的参数为1000*800,MyPanel1.MeasureOverride返回的参数为80*50.分析一下结果:MyPanel1实际的画出来的大小是5020
21、0,明显是被旋转了90度。运行起来,你会发现最终的MyPanel1.DesiredSize在Measure过程之后为70220,也就是说,它是被Transform之后的大小,明显是被旋转过的。另外,观察MyPanel.MeasureOverride传入的参数,为200980,根据上一节对Measure过程的分析,MeasureOverride传入的参数宽为200是可预知的,因为我们设置了MyPanel1.Width为200,但Height为980,明显是MyPanel.Measure传入的宽1000减去2*10等于980,看来在进入MeasureOverride之前,Layout系统也处理了L
22、ayoutTransform对Measure过程的影响,它希望MeasureOverride不要关心自身LayoutTransform的影响。MeasureOverride结束后,返回值为8050,根据上一节对Measure过程的分析,宽为80被调节为符合自己的设置,为200,由于高没有设置,这个50肯定会保留,因此最后在没有Transform之前的DesiredSize应该是22070,然而基类会将MeasureOverride返回的大小再进行一次Transform,达到最终的DesiredSize的大小,以便Arrange的时候分配合适的空间来容纳MyPanel的大小。如果你将上面例子的M
23、yPanel1.LayoutTransform设置成ScaleTransform:Window x:Class=WpfApplication1.MainWindowxmlns=xmlns:x=Title=MainWindowHeight=522Width=594Loaded=Window_Loadedxmlns:my=clr-namespace:WpfApplication1Canvas my:MyPanelParent x:Name=myPanelParent1Height=400Width=400Background=LimeCanvas.Left=10Canvas.Top=10my:My
24、Panel Margin=10x:Name=myPanel1Background=RedWidth=200my:MyPanel.LayoutTransform ScaleTransform ScaleX=2ScaleY=2/my:MyPanel.LayoutTransform/my:MyPanel my:MyPanel Margin=10x:Name=myPanel2Background=RedMinWidth=150MaxWidth=250/my:MyPanelParent/Canvas/Window然后再观察myPanel.MeasureOverride传入的参数,为200390,首先20
25、0是可预知的,因为设置了Width属性,而390是怎么回事呢,其实为Measure传入的1000800的高800减去Margin为20后得到780,然后根据LayoutTransform将高缩小2倍之后得到的390,因此传入的参数就是200390,可见,Layout系统,在进入MeasureOverride之前,他希望,MeasureOv erride只关心内容怎么布置,而不需要关心基类属性的设置对MeasureOverride的影响。由于MeasureOverride的返回值依然是8050,可推理,80被调节为200,50被保留,没有Transform之前的值应该是20050。因为基类还要进
26、行Transform,因此,内容区域的真实的大小应该是400100,再加上Margin之后,最终的DesiredSize肯定为420*120,你可以尝试调试给出的代码。3.Measure过程的总结Measure过程的总结通过上面的过程分析,我相信你或多或少对WPF的Layout系统的Measure过程有了更进一步的了解,其实还有一些因素影响Measure的过程,比如UseLayoutRounding属性,在进入MeasureOverride之前和之后,基类都被将参数根据DPI进行Rounding,这个过程知道就行了,不需要在自己的MeasureOverride里面关心。我们总结一下哪些属性和参
27、数会影响Measure的过程:MyPanel.Measure传入的参数availableSize,MyPanel的MinWidth,Width,MaxWidth,Margin,UseLayoutRounding,LayoutTransform,MeasureOverride的返回值。Measure过程相关问题解答Q1:什么是Layout Slot?什么时候能获取到?在哪里获取?Layout Slot就是调用Arrange方法的时候,传入的参数finalRect,这是父分配给子的容纳Margin以及内容区域的矩形空间;当Arrange过程结束后,你可以拿到;通过调用静态类LayoutInform
28、ation.GetLayoutSlot(FrameworkElement element)方法可以拿到。Q2:什么是Layout Clip?什么时候能获取到?在哪里获取?Layout Clip只的是当内容区域要绘制的大小,大于LayoutSlot刨去Margin区域后的大小,这时候,内容区域就会被Clip,超出的部分会被Clip掉,而剩下的可显示的部分就是Layout Clip,他是一个Geometry。Arrange过程结束后,可以拿到;通过调用静态类LayoutInformation.GetLayoutClip(FrameworkElement element)方法可以拿到。如果内容区域可
29、以完全显示在Layout Slot刨去Margin的区域内,LayoutClip为Null。Q3:在父的MeasureOverride当中调用孩子的Measure方法时,传入的参数有没有什么限制?有,确保availableSize.Width和Height不是NaN;但可以是Infinity Q4:在进入自己的MeasureOverride方法后,面对参数我该咋办?首先,心里应该明白,传入的参数已经是基类刨去自己的Margin,并且考虑了基类影响Measure过程的属性之后的值。其次,看自身有没有自定义的,并且影响Layout的属性,根据自己的内容要求,或者孩子的情况,调用孩子的Measure方法,并传入希望孩子限定在多大范围内空间。最后,返回一个自己期望的Size。这里应该注意的点:1.调用孩子的Measure方法时,传入的参数,是你限定孩子的最大空间,用来显示孩子的Margin以及内容区域的,而孩子不管最终期望的大小有多少,都会被你给他的availableSize裁剪。2.根据自身的策略返回一个期望的值,这个期望的值应该是在自己的MinWidth,Width,MaxWidth限定的范围呢,如果没有,基类还会强行调整。3.基类调整后的值还会被父传入的availableSize再次调整,
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论