WPF Layout 系统概述——Arrange
Arrange过程概述
普通基类属性对Arrange过程的影响
我们知道Measure过程是在确定DesiredSize的大小,以便Arrange过程参考这个DesiredSize,确定给MyPanel分配多少空间,但是DesiredSize只是作为参考,在有些用例下,MyPanelParent在调用MyPanel.Arrange的时候,会根据父的实际策略指定MyPanel.Arrange方法的参数,而不是直接指定MyPanel.DesiredSize的大小,比如Grid。因此,最终MyPanel应该如何呈现,决定权还是在Layout系统的Arrange过程当中。那么Arrange过程最终确定哪些数据呢?主要是MyPanel.RenderSize,MyPanel.VisualOffset以及VisualTransform三个属性。再说的清楚点就是确定Layout
Slot以及最终绘制的位置和区域。而LayoutSlot本质上就是MyPanelParent调用MyPanel.Arrange时,传入的参数finalRect.
请看下面的设置:
< Window x:Class="WpfApplication1.MainWindow" Title="MainWindow" < Canvas > < my:MyPanelParent x:Name="myPanelParent1" < my:MyPanel x:Name="myPanel1" < my:MyPanel x:Name="myPanel2" </ my:MyPanelParent > </ Canvas > </ Window > |
public class MyPanelParent:Panel { protected override System.Windows.Size { foreach (UIElement in this .InternalChildren) { item.Measure( new Size(1000, } return availableSize; } protected override System.Windows.Size { double x foreach (UIElement in this .InternalChildren) { item.Arrange( new Rect(x, x } return finalSize; } } public class MyPanel { protected override System.Windows.Size { foreach (UIElement in this .InternalChildren) { item.Measure(availableSize); } return new Size(50, //MyPanel期望50×50的矩形 } protected override System.Windows.Size { double xCordinate foreach (UIElement in this .InternalChildren) { item.Arrange( new Rect( new Point(xCordinate, xCordinate } return finalSize; } } |
根据上面的设置,我们没有设置一些相关的属性,比如Width,MinWidth,MaxWidth,另外,调用MyPanel.Measure时传入的值也是比较大,可见MyPanelParent给孩子MyPanel足够的空间去安排自己的内容,而MyPanel.MeasureOverride返回了一个50×50的大小,根据『普通基类属性对Measure过程的影响』一节对影响Measure的参数的优先级的总结,我们推理,目前MyPanel.DesiredSize应该是50×50,因为没有Margin。另外,Arrange的时候调用MyPanel.Arrange传入的参数也正好是MyPanel.DesiredSize,MyPanel.ArrangeOverride的返回值依然是传入的参数大小,没有变化,因此,我们推理,运行起来的MyPanel大大小就应该是50×50的红色矩形区域,具体请看下图:
如果你尝试修改MyPanel.HorizontalAlignment,你会发现,这个属性不管怎么设置,MyPanel的位置和大小始终不会改变,这究竟是为什么呢?
其实,HorizontalAlignment是控制当MyPanel的最终希望的大小,小于父MyPanelParent分配的LayoutSlot(调用MyPanel.Measure方法传入的参数finalRect)时,MyPanel被安置的位置或大小。
如果将上面的例子的MyPanel.Arrange的参数改为100×100:
public class MyPanelParent:Panel { protected override System.Windows.Size { foreach (UIElement in this .InternalChildren) { item.Measure( new Size(1000, } return availableSize; } protected override System.Windows.Size { double x foreach (UIElement in this .InternalChildren) { item.Arrange( new Rect(x, x } return finalSize; } } |
你看到的效果,就是另外一番景象了。这时候MyPanel期望的是50×50,LayoutSlot为100×100,那么50×50就可以根据HorizontalAlignment或者VerticalAlignmet的设置起作用了。
HorizontalAlignment=Horizontal |
HorizontalAlignment=Stretch |
如果让LayoutSlot为30×30,整个Panel又会是什么样子呢?可以预见的是HorizontalAlign是不会起作用了,因为期望的大小还要大于父分配的layout slot.运行后结果:
接下来,我们再添加修改一下设置,最后给出影响Arrange过程的因素,以及最终Render的结果。
请看代码:
< Window x:Class="WpfApplication1.MainWindow" Title="MainWindow" < Canvas > < my:MyPanelParent x:Name="myPanelParent1" < my:MyPanel x:Name="myPanel1" < my:MyPanel x:Name="myPanel2" </ my:MyPanelParent > </ Canvas > </ Window > |
public class MyPanel { protected override System.Windows.Size { foreach (UIElement in this .InternalChildren) { item.Measure(availableSize); } return new Size(50, } protected override System.Windows.Size { double xCordinate foreach (UIElement in this .InternalChildren) { item.Arrange( new Rect( new Point(xCordinate, xCordinate } return new Size(80, //MyPanel希望安排的最终大小 } } public class MyPanelParent:Panel { protected override System.Windows.Size { foreach (UIElement in this .InternalChildren) { item.Measure( new Size(1000, } return availableSize; } protected override System.Windows.Size { double x foreach (UIElement in this .InternalChildren) { item.Arrange( new Rect(x, //父安排的LayoutSlot x } return finalSize; } } |
运行效果如下所示:
设置分析:
MyPanel.Width=90,
MyPanel.DesiredSize = 90*50(通过Measure过程之后)
MyPanel.Arrange传入的finalRect参数为100×100,大于DesiredSize
MyPanel.ArrangeOverride返回80×80
分析结果:
最终绘制的大小是80×80,不是期望的DesiredSize=90×50(根据Measure过程分析得到),而是MyPanel.ArrangeOverride返回的80×80,可见ArrangeOverride的返回值策略跟MeasureOverride返回值的处理是不一样的,他是多少就是多少,不会受MinWidth,Width,MaxWidth的限制。然而,不受限制这一说法并不完全正确,当MyPanel.Width小于80时,绘制的大小会跟随Width的设置,也就是说取两者的最小值。前面说过Arrange过程是在确定RenderSize,那么Arrange完成后,MyPanel.RenderSize是多少呢?为MyPanel.RenderSize
= 80*80,也就是ArrangeOverride的返回值;
如果,你将MyPanel.MinWidth设置为30,Width设置为50,限制MyPanel的大小,绘制区域就变小了,成了50:
跟Measure过程一样,上面所做的一切必须限制在MyPanel.Arrange传入的参数finalRect,也就是LayoutSlot当中。可以预见的是,如果父调用MyPanel.Arrange方法传入的不是100×100,而是30×30,那么,最终的可见绘制区域就是30×30.
!Arrange_Window_5.PNG!
请猜一下上面两种修改过后,MyPanel.RenderSize变化成多少了?
有人会回答:如果设置了MyPanel.Width为50,显示的也为50×50,MyPanel.RenderSize就为50×50;传入MyPanel.Arrange的参数为30×30时,显示的也是30×30,MyPanel.RenderSize就为30×30.
完全错误!上面两种设置后,MyPanel.RenderSize依然是80×80,也就是ArrangeOverride的返回值。
其实RenderSize是没有Clip的绘制区域的大小,而实际看见的绘制区域是把RenderSize经过MyPanel.Width以及LayoutSlot进行Clip之后的效果。
通过下面的流程图,描述一下Arrange过程具体做了哪些事情:
通过上面的流程图,我们已经了解了Arrange过程大体所做的事情。按照对Measure过程的阐述,总结一下Arrange过程的主要点:
1. 确定RenderSize,以及最终MyPanel被安置的空间。RenderSize就是ArrangeOverride的返回值,没还有被裁剪过的值。
2. 确定Client区域和Ink区域,根据HorizontalAlignment和VerticalAlignment确定Ink区域的在Client区域当中的位置和所占据的大小。Client区域是LayoutSlot减去Margin;Ink区域是ArrangeOverride返回的值被Width剪切之后,必须是两者的最小值。
3. 确定Visual基类上面若干跟具体Render相关的设置,比如VisualOffset,VisualTransform,Clip等等。
Transform对Arrange的影响
跟Measure过程类似,Layout系统不希望ArrangeOverride关心自身Transform的影响。所有对RenderTransform以及LayoutTransform设置,在ArrangeOverride退出后,基类会处理,并且根据设置调节VisualTransform。而RenderSize,LayoutSlot都不会受Transform的影响。
Arrange过程的总结
除了上面提到的属性或者参数对Arrange过程有影响外,其实内容,还有更多属性影响这个过程,总结一下哪些属性和参数会影响Arrange过程:MyPanel.Arrange传入参数finalRect,Mypanel.MinWidth,Width,MaxWidth,Margin,DesiredSize,HorizoantalUseLayoutRounding,ClipToBounds,Clip,LayoutTransform,RenderTransform,RenderTransformOrigin。
Arrange过程相关问题回答
Q1:在父的ArrangeOverride当中调用孩子的Arrange方法时,传入的参数有没有什么限制?
除了向Q8所说,如需要根据自己的策略,决定给孩子分配多大的空间之外,还需要保证这个finalRect参数不能为NaN,Infinity。因为,Arrange过程是最终确定孩子的LayoutSlot的时机,必须保证传入的参数是个确定的值。
Q2:在进入自己的ArrangeOverride方法后,面对参数我该咋办?
跟Measure过程类似。
首先,心里应该明白,传入的参数已经是基类刨去自己的Margin,并且考虑了基类影响Arrange过程的属性之后的值。
其次,看自身有没有自定义的,并且影响Layout的属性,根据自己的内容要求,或者孩子的情况,调用孩子的Arrange方法,并传入希望孩子限定在多大范围内空间。
最后,返回一个自己期望的Size,这个Size将会是RenderSize的值,不会被调整。
这里应该注意的点:
1. 调用孩子的Arrange方法时,传入的参数,是你限定孩子的最大空间,用来显示孩子的Margin以及内容区域的,而孩子不管最终想要的RenderSize有多少,都会被你给他的finalRect裁剪。
2. 根据自身的策略返回一个finalSize,这个期望的值应该是小于等于Width,MaxWidth,如果没有,基类还会强行调整,总是取最小值。
3. 基类调整后的值还会被父传入的LayoutSlot再次调整,返回值不能大于父传入的参数减去Margin之后的值。
4. 只有当ArrangeOverrid的返回值小于LayoutSlot刨去Margin区域之后的空间,HorizontalAlignment和VeriticalAlignment才会起作用,否则不会起作用。因为,ArrangeOverride返回值,相当去内容区域,而LayoutSlot刨去Margin的区域是Client区域,而Alignment就是将内容区域安放在Client区域的过程。
Q3:ArrangeOverride的返回值有没有什么限制?
可以为NaN或则PositiveInfinity。
Q4:RenderSize究竟是什么?
从代码实现当中说,RenderSize就是ArrangeOverride的返回值。
从逻辑上讲,RenderSize就是没有被Clip过的绘制区域的大小。最终显示出来的大小,是RenderSize经过自己的设置,以及父的限制之后,呈现出来的大小。
Q5: ContentControl的MeasureOverride和ArrangeOverride过程有没有什么特殊之处?
没有,他的Layout策略继承自Control,仅仅是将Content属性的设置作为Control的Visual孩子而已。
Q6: Control的MeasureOverride和ArrangeOverride过程是什么样的?
MeasureOverride:Control的父给多大的空间,Control就将其全部给自己的孩子,并返回孩子的DesiredSize。
ArrangeOVerride:Control的父分配给孩子多大的空间,Control就将其全部给自己的孩子,并返回父给的内容区域作为RenderSize。
总结一点:Control的空间,就是他的孩子的空间。
Q7: ContentPresenter的MeasureOverride和ArrangeOverride是什么样子的?
跟Control一样。
Q8: 能不能在MeasureOverride或者ArrangeOverride当中调用孩子的孩子,也就是孙子的Measure或者Arrange方法?
目前来看,WPF/SL是允许这样做的。但是,通过研究Layout系统之后,发现这个系统是非常庞大和复杂,另外,Arrange的时候, 孩子的位置总是基于自己的父,另外,内容会发一些基于父的事件,比如,ChildDesiredSizeChanged之类的事件。因此,个人觉得,最好不要这样做,谁也保证不了这样的做法是否不会出问题,无疑中也增加了代码的可读性。
影响WPF Layout的属性总结以及跟Silverlight的不同之处
WPF |
Silverlight |
|
Measure过程 |
Margin,MinWidth,Width |
Margin,MinWidth,Width, |
Arrange过程 |
Margin,MinWidth,Width |
Margin,MinWidth,Width |
WPF Layout 系统概述——Arrange的更多相关文章
- WPF Layout 系统概述——Measure
原文:WPF Layout 系统概述--Measure 前言 在WPF/Silverlight当中,如果已经存在的Element无法满足你特殊的需求,你可能想自定义Element,那么就有可能会面临重 ...
- WPF/Silverlight Layout 系统概述——Arrange(转)
Arrange过程概述 普通基类属性对Arrange过程的影响 我们知道Measure过程是在确定DesiredSize的大小,以便Arrange过程参考这个DesiredSize,确定给MyPane ...
- WPF Layout 系统概述 MeasureOverride和ArrangeOverride
说的非常的好:多参考!!! https://blog.csdn.net/nncrystal/article/details/47416339 https://www.cnblogs.com/dingl ...
- Understanding the WPF Layout System
Many people don't understand how the WPF layout system works, or how that knowledge can help them in ...
- WPF/Silverlight Layout 系统概述——Measure(转)
前言 在WPF/Silverlight当中,如果已经存在的Element无法满足你特殊的需求,你可能想自定义Element,那么就有可能会面临重写MeasureOverride和ArrangeOver ...
- 关于WPF你应该知道的2000件事
原文 关于WPF你应该知道的2000件事 以下列出了迄今为止为WPF博客所知的2,000件事所创建的所有帖子. 帖子总数= 1,201 动画 #7 - 基于属性的动画 #686 - 使用动画制作图像脉 ...
- WPF学习之路初识
WPF学习之路初识 WPF 介绍 .NET Framework 4 .NET Framework 3.5 .NET Framework 3.0 Windows Presentation Found ...
- [ExtJS5学习笔记]第十九节 Extjs5中通过设置form.Panel的FieldSet集合属性控制多个field集合
本文地址:http://blog.csdn.net/sushengmiyan/article/details/39209533 官方例子:http://docs.sencha.com/extjs/5. ...
- 201771010126 王燕《面向对象程序设计(Java)》第十四周学习总结(测试程序11)
实验十四 Swing图形界面组件 理论部分: 不使用布局管理器 有时候可能不想使用任何布局管理器,而只 是想把组件放在一个固定的位置上.下面是将一 个组件定位到某个绝对定位的步骤: 1)将布局管理器 ...
随机推荐
- Objective-C基础笔记(8)Foundation经常使用类NSString
一.创建字符串的方法 void stringCreate(){ //方法1 NSString *str1 = @"A String!"; //方法2 NSString *str2 ...
- 一起学Python:TCP简介
TCP介绍 TCP协议,传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的.可靠的.基于字节流的传输层通信协议,由IETF的RFC 793 ...
- signed 与 unsigned 有符号和无符号数
unsigned int a = 0; unsigned int b = -1; // b 为 0xffffffff unsigned int c = a - 1; // c 为 0xffffffff
- 查看MySQL数据的连接
show processlist; select host from information_schema.processlist; 查看那台机器及连接数 select host, cur ...
- [Angular] Create dynamic content with <tempalte>
To create a dynamic template, we need to a entry component as a placeholder. Then we can use entry c ...
- 【codeforces 750B】New Year and North Pole
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- echarts改变颜色属性的demo
一:柱状图改变颜色 图片.png 代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8&qu ...
- reflect(反射)了解一点点
A a = new A() 这个代码在程序编译阶段,会自己主动定位到A类上,而且新建一个A的实例. 可是假设我们希望程序在执行时.动态的创建一个A的实例.此时程序仅仅知道要从名字叫A的类中创建一个实例 ...
- POJ 1985 - 树的直径
传送门 题目大意 给一颗n个点的树,求树的直径(最长的一条链) 题解 先随便找一个点u,dfs出离它最远的点v 于是有以下情况: 直径就是这条链 直径经过u,是这条链的延长 直径不经过u 只需要从v再 ...
- javascript数据结构与算法 零(前记+前言)
前记 这本书Data Structure and Algorithm with Javascript 我将其翻译成<< javascript 数据结构和算法>> 为什么这么翻译 ...