[深刻浅出Windows 10]布局原理

5.2 布局原理

    不少时候在编写程序界面的时候都会忽略了应用布局的重要性,仅仅只是把布局看做是对UI元素的排列,只要能实现布局的效果就能够了,可是在实际的产品开发中这是远远不够的,你可能面临要实现的布局效果要比常规布局更加复杂,这就须要对布局的技术知识有深刻的理解和掌握才可以实现。要实现一个布局的效果,可能会有不少总布局方案,咱们该怎么去选择实现的方法?若是要实现的一个布局效果是比较复杂的,咱们该怎么去对这种布局规律进行封装?要解决这些问题,首要的问题就是须要咱们对程序的布局原理有着深刻的理解。html

5.2.1 布局的意义

    布局是页面编程的第一步,是从整体的方向去把握页面上UI元素的显示。布局其实一个一个应用程序开发里面很是重要的一部分,这块的知识也是每每会被开发者所忽视。随着Windows 10支持这很是多的设备和屏幕分辨率,布局显得原来越重要,也将会变得更加复杂,咱们很是有必要去了解作程序的界面布局有什么样的意义,这不只仅是界面显示能够就完事了。下面来看一下应用程序的布局有什么样的意义:编程

    (1)代码逻辑微信

    良好的布局会使得代码逻辑很是清晰,差的布局方案会让页面代码逻辑很混乱。若是你只是靠拖拉控件来作Windows 10的布局,这个程序的界面布局确定会变成很糟糕,因此好的布局方案,必定要基于对各类布局控件的理解,而后充分地它们的特性去实现布局的效果。框架

    (2)效率性能ide

    布局不只仅是界面UI的事情,它甚至会影响到程序的运行效率。固然简单的几个控件的页面布局,对程序效率性能的影响是微乎其微的,可是若是你的界面要展现大量的控件的时候,这时候布局的好坏就会直接影响到程序的效率。好的布局实现逻辑会让程序即便在有大量控件的页面也能流程的运行。布局

    (3)动态适配性能

    动态适配包括两个方面,一个是Windows 10的多种分辨率的界面的适配,另一个是页面的控件是不肯定的也会产生动态适配的问题。好的布局方案,可使应用程序能够兼容各类分辨率的设别,受到不一样分辨率所影响的页面,也是能够经过布局的技巧来解决的,保证不一样的分辨率下面都是符合产品的显示效果。还有一个就是动态产生的控件,这个是指你的页面上会根据不一样的状况显示不一样的内容,这时候在作布局的时候就要思考如何对付这些会变化的页面。测试

    (4)实现复杂的布局this

    有时候页面须要实现一些复杂的布局效果,好比像圆圈必定排列控件,Windows 10里面是没有这样的布局控件支持这种复杂的布局效果的,这时候就须要去自定义布局的规律来解决这样的问题。可否自定义布局控件去实现复杂的布局效果,这就要看你对Windows 10的布局技术的掌握程度了。spa

5.2.2 布局系统

    布局系统是指对Windows 10的布局面板所进行的布局过程的运做原理的统称。布局实际上是一个在 Windows 10应用中调整对象大小和定位对象的过程。要定位可视化对象,必须将它们放置于 Panel 或其余布局面板中。Panel类是全部布局面板的父类,系统的布局面板Canvas、StackPanel、Grid和RelativePanel都是Panel类的子类,继承了全部Panel类的特性。Panel类定义了在屏幕上绘制全部的面板里面的成员(Children属性)的布局行为。这是一个计算密集型过程,即 Children 集合越大,执行的计算次数就越多,也就是面板里面的元素越多,布局系统布局的整个过程的时间就会越长。全部的布局面板类都是在Panel的基础上添加了相应的布局规律的,在Panel类的基础上继续封装的布局面板是为了更好地解决一些布局规律的问题,实际上这样的封装是增长复杂性,对性能形成必定的损失的。因此若是不须要较为复杂的布局面板(如 Grid),则可使用构造相对简单的布局(如 Canvas),这种布局可产生更佳的性能。

    简单地说,布局是一个递归系统,实如今屏幕上对元素进行大小调整、定位和绘制。在整个布局的过程当中,布局系统对布局面板成员的处理分为两个过程:第一个是测量处理过程,第二个是排列处理过程。测量处理过程是肯定每一个子元素所需大小的过程。排列处理过程是最终肯定每一个子元素的大小和位置的过程。每当面板里面的成员改变其位置时,布局系统就可能触发一个新的处理过程,从新处理上面所说的两个过程。不论什么时候调用布局系统,都会发生如下一系列的操做:

    一、第一次递归遍历测量每一个布局面板子元素(UIElement类的子类控件)的大小。

    二、计算在 FrameworkElement类的子类控件元素上定义的大小调整属性,例如 Width、Height 和 Margin。

    三、应用布局面板特定的逻辑,例如StackPanel面板的水平布局。

    四、第二次递归遍历负责把子元素排到对于本身的相对的位置。

    五、把全部的子元素绘制到屏幕上。

    若是其余子元素添加到了集合中、子元素的布局属性(如 Width 和 Height)发生了改变或调用了 UpdateLayout 方法,均会再次调用该过程。所以,了解布局系统的特性就很重要,由于没必要要的调用可能致使应用性能变差。下面的内容将会详细地演示这个布局的过程。

5.2.3 布局系统的重要方法和属性

在Windows 10中,布局不只仅是布局面板的要作的事情,布局面板是在负责把这个布局的过程组织起来,而在整个布局的过程当中对于布局面板里面的元素都要通过一个从最外面到最里面的一个递归的测量和排列的过程。在研究这个递归的排列和测试的过程,先来了解一下,基本的控件上关于布局的一些重要的方法和属性。

Windows 10的UI元素有两个很是重要的基类UIElement类和FrameworkElement类,他们的继承层次结构以下:

 Windows.UI.Xaml.DependencyObject

   Windows.UI.Xaml.UIElement

     Windows.UI.Xaml.FrameworkElement

(1)UIElement

UIElement类是具备可视外观并能够处理基本输入的大多数对象的基类。关于布局,UIElement类有两个很是重要的属性——DesiredSize和RenderSize属性和两个很是重要的方法——Measure 方法和Arrange方法。

DesiredSize属性:这是一个只读的属性,类型是Size类,表示在布局过程的测量处理过程当中计算的大小。

RenderSize属性:这是一个只读的属性,类型是Size类,表示UI元素最终呈现大小,RenderSize和DesiredSize并不必定是相等的。RenderSize就是其ArrangeOverride方法的返回值。

public void Measure(Size availableSize)方法:Measure方法所作的事情是更新 UIElement 的 DesiredSize属性,测量出UI元素的大小。若是在该UI元素上实现了FrameworkElement.MeasureOverride(System.Windows.Size)方法,将会用此方法以造成递归布局更新。参数availableSize表示:父对象能够为子对象分配的可用空间。子对象能够请求大于可用空间的空间,若是该特定面板中容许滚动或其余调整大小行为,则提供的大小能够适应此空间。

public void Arrange(Rect finalRect)方法:Arrange方法所作的事情是定位子对象并肯定 UIElement 的大小,也就是DesiredSize属性的值。若是在该UI元素上实现了FrameworkElement. ArrangeOverride(System.Windows.Size)方法,将会用此方法以造成递归布局更新。参数finalRect表示:布局中父对象为子对象计算的最终大小,做为 System.Windows.Rect 值提供。

(2)FrameworkElement

     FrameworkElement类是UIElement类的子类,为 Windows 10布局中涉及的对象提供公共 API 的框架。FrameworkElement类有两个和布局相关的虚方法MeasureOverride 方法和ArrangeOverride方法。若是已经存在的布局面板没法知足特殊的布局需求,你可能须要自定义布局面板,就须要重写MeasureOverride和ArrangeOverride两个方法,而这两个方法是Windows 10的布局系统提供给用户的自定义接口,下面来看下这两个方法的含义。

    protected virtual Size MeasureOverride(Size availableSize)提供 Windows 10布局的度量处理过程的行为,能够重写该方法来定义其本身的度量处理过程行为。参数availableSize表示对象能够赋给子对象的可用大小,能够指定无穷大值 (System.Double.PositiveInfinity),以指示对象的大小将调整为可用内容的大小,若是子对象所计算出来的大小比availableSize大,那么将会被截取出availableSize大小的部分。返回结果表示此对象在布局过程当中基于其对子对象分配大小的计算或者基于固定面板大小等其余因素而肯定的它所需的大小。

protected virtual Size ArrangeOverride(Size finalSize)提供 Windows 10布局的排列处理过程的行为,能够重写该方法来定义其本身的排列处理过程行为。参数finalSize表示父级中此对象应用来排列自身及其子元素的最终区域。返回结果表示元素在布局中排列后使用的实际大小。

5.2.4 测量和排列的过程

Windows 10的布局系统是一个递归系统,它老是以Measure方法开始,最后以Ararnge方法结束。假设在整个布局系统里面只有一个对象,这个对象在加载到界面以前会先调用Measure方法来测量对象的大小,最后再调用Ararnge方法来安排对象的位置完成了整个过程的布局。可是现实中每每是一个对象里面包含了多个子对象,子对象里面也包含着子对象,如此递归下去,直到最底下的对象。布局的过程就是从最顶层的对象开始测量,最顶层的对象的测量过程又会调用它的子对象的测量方法,如此递归直到最底下的对象。测量的过程完成以后,则开始排列的过程,排列的过程也是和测量的过程的原理同样,一直递归下去直到最底下的对象。下面经过一个示例来模拟这个过程。

示例里面建立了两个类,TestPanel类用来模拟最外面的布局面板,做为父对象的角色;TestUIElement类用来模拟做为布局面板的元素,做为最底下子对象的角色。TestUIElement类和TestPanel类都继承了Panel类,实现了MeasureOverride和ArrangeOverride方法,并打印出相关的日志用于跟踪布局的详细状况。

代码清单5-8模拟测量和排列的过程(源代码:第5章\Examples_5_8)

TestUIElement.cs文件主要代码
------------------------------------------------------------------------------------------------------------------
    public class TestUIElement : Panel
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            Debug.WriteLine("进入子对象" + this.Name + "的MeasureOverride方法测量大小");
            return availableSize;
        }
        protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
        {
            Debug.WriteLine("进入子对象" + this.Name + "的ArrangeOverride方法进行排列");
            return finalSize;
        }
}
TestPanel.cs文件主要代码
-----------------------------------------------------------------------------------------------------------------
    public class TestPanel : Panel
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            Debug.WriteLine("进入父对象" + this.Name + "的MeasureOverride方法测量大小");
            foreach (UIElement item in this.Children)
            {
                item.Measure(new Size(120, 120));//这里是入口
                Debug.WriteLine("子对象的DesiredSize值  Width:" + item.DesiredSize.Width + " Height:" + item.DesiredSize.Height);
                Debug.WriteLine("子对象的RenderSize值  Width:" + item.RenderSize.Width + " Height:" + item.RenderSize.Height);
            }
            Debug.WriteLine("父对象的DesiredSize值  Width:" + this.DesiredSize.Width + " Height:" + this.DesiredSize.Height);
            Debug.WriteLine("父对象的RenderSize值  Width:" + this.RenderSize.Width + " Height:" + this.RenderSize.Height);
            return availableSize;
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            Debug.WriteLine("进入父对象" + this.Name + "的ArrangeOverride方法进行排列");
            double x = 0;
            foreach (UIElement item in this.Children)
            {
                //排列子对象
                item.Arrange(new Rect(x, 0, item.DesiredSize.Width, item.DesiredSize.Height));
                x += item.DesiredSize.Width;
                Debug.WriteLine("子对象的DesiredSize值  Width:" + item.DesiredSize.Width + " Height:" + item.DesiredSize.Height);
                Debug.WriteLine("子对象的RenderSize值  Width:" + item.RenderSize.Width + " Height:" + item.RenderSize.Height);
            }
            Debug.WriteLine("父对象的DesiredSize值  Width:" + this.DesiredSize.Width + " Height:" + this.DesiredSize.Height);
            Debug.WriteLine("父对象的RenderSize值  Width:" + this.RenderSize.Width + " Height:" + this.RenderSize.Height);
            return finalSize;
        }
}

建立了TestUIElement类和TestPanel类以后,接下来要在UI上使用这两个类,把TestPanel看做是布局面板控件来使用,把TestPanel看做是普通控件来使用,而后观察打印出来的运行日志。要添加这两个控件须要如今xaml页面上把这两个空间所在的空间引入进去再进行调用,以下面的代码所示:

MainPage.xaml文件主要代码
-----------------------------------------------------------------------------------------------------------------
    ……省略若干代码 下面引入控件空间
xmlns:local="using:MeasureArrangeDemo"
……省略若干代码  下面调用控件布局
    <StackPanel>
        <Button Content="改变高度" Click="Button_Click_1"></Button>
        <local:TestPanel x:Name="panel" Height="400" Width="400" Background="White" >
            <local:TestUIElement  x:Name="element1" Width="60" Height="60" Background="Red" Margin="10"/>
            <local:TestUIElement x:Name="element2"  Width="60" Height="60" Background="Red" />
        </local:TestPanel>
</StackPanel>

程序在Debug的状态下运行以后,在Visual Studio的输出窗口能够看到打印出来的日志。日志的详细状况以下:

/*日志开始*/

    进入父对象panel的MeasureOverride方法测量大小

    进入子对象element1的MeasureOverride方法测量大小

    子对象的DesiredSize值  Width:80 Height:80

    子对象的RenderSize值  Width:0 Height:0

    进入子对象element2的MeasureOverride方法测量大小

    子对象的DesiredSize值  Width:60 Height:60

    子对象的RenderSize值  Width:0 Height:0

    父对象的DesiredSize值  Width:0 Height:0

    父对象的RenderSize值  Width:0 Height:0

    进入父对象panel的ArrangeOverride方法进行排列

    进入子对象element1的ArrangeOverride方法进行排列

    子对象的DesiredSize值  Width:80 Height:80

    子对象的RenderSize值  Width:60 Height:60

    进入子对象element2的ArrangeOverride方法进行排列

    子对象的DesiredSize值  Width:60 Height:60

    子对象的RenderSize值  Width:60 Height:60

    父对象的DesiredSize值  Width:400 Height:400

    父对象的RenderSize值  Width:0 Height:0

/*日志结束*/

从打印出来的日志能够很清楚地看到整个布局过程的步骤(如图5.25所示),以及布局过程当中DesiredSize值和RenderSize值的变化状况。

        从布局的过程当中能够总结出下面的结论:

1.       测量的过程是为了确认DesiredSize的值,最终是要提供给排列的过程去使用。

2.       DesiredSize是根据Margin,WidthHeight等属性来决定。

3.       排列的过程肯定RenderSize,以及最终子对象被安置的空间。RenderSize就是ArrangeOverride的返回值,没还有被裁剪过的值。

4.       Margin,WidthHeight等属性只是控件表面上的属性,而实际掌控住这些效果的是布局的测量排列过程。

5.       Margin,WidthHeight等属性的改变会从新触发布局的过程。

 

本文来源于《深刻浅出Windows 10通用应用开发》

 

源代码下载:http://vdisk.weibo.com/u/2186322691

 

目录:http://www.cnblogs.com/linzheng/p/5021428.html

 

欢迎关注个人微博@WP林政   微信公众号:wp开发(号:wpkaifa)

 

Windows10/WP技术交流群:284783431

相关文章
相关标签/搜索