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"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="458" Width="442" Loaded="Window_Loaded" xmlns:my="clr-namespace:WpfApplication1">
<Canvas>
<my:MyPanelParent x:Name="myPanelParent1" Height="400" Width="400" Background="Lime" Canvas.Left="10" Canvas.Top="10">
<my:MyPanel x:Name="myPanel1" Background="Red"/>
<my:MyPanel x:Name="myPanel2" Background="Red"/>
</my:MyPanelParent>
</Canvas>
</Window>
    public class MyPanelParent:Panel
{
protected 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(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 : Panel
{
protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
{
foreach (UIElement item in this.InternalChildren)
{
item.Measure(availableSize);
}
return new Size(50, 50);//MyPanel期望50×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(xCordinate, 0), item.DesiredSize));
xCordinate += item.DesiredSize.Width;
}
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 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(System.Windows.Size finalSize)
{
double x = 0;
foreach (UIElement item in this.InternalChildren)
{
item.Arrange(new Rect(x, 0, 100,100));
x += 100;
} 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"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="458" Width="442" Loaded="Window_Loaded" xmlns:my="clr-namespace:WpfApplication1">
<Canvas>
<my:MyPanelParent x:Name="myPanelParent1" Height="400" Width="400" Background="Lime" Canvas.Left="10" Canvas.Top="10">
<my:MyPanel x:Name="myPanel1" Background="Red" HorizontalAlignment="Left" Width="90"/>
<my:MyPanel x:Name="myPanel2" Background="Red"/>
</my:MyPanelParent>
</Canvas>
</Window>
    public class MyPanel : Panel
{
protected override System.Windows.Size MeasureOverride(System.Windows.Size availableSize)
{
foreach (UIElement item in this.InternalChildren)
{
item.Measure(availableSize);
}
return new Size(50, 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(xCordinate, 0), item.DesiredSize));
xCordinate += item.DesiredSize.Width;
}
return new Size(80, 80);//MyPanel希望安排的最终大小
}
} public class MyPanelParent:Panel
{
protected 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(System.Windows.Size finalSize)
{
double x = 0;
foreach (UIElement item in this.InternalChildren)
{
item.Arrange(new Rect(x, 0, 100,100));//父安排的LayoutSlot
x += 100;
} 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: ContentControlMeasureOverrideArrangeOverride过程有没有什么特殊之处?

没有,他的Layout策略继承自Control,仅仅是将Content属性的设置作为Control的Visual孩子而已。

Q6: ControlMeasureOverrideArrangeOverride过程是什么样的?

MeasureOverride:Control的父给多大的空间,Control就将其全部给自己的孩子,并返回孩子的DesiredSize。

ArrangeOVerride:Control的父分配给孩子多大的空间,Control就将其全部给自己的孩子,并返回父给的内容区域作为RenderSize。

总结一点:Control的空间,就是他的孩子的空间。

Q7: ContentPresenterMeasureOverrideArrangeOverride是什么样子的?

跟Control一样。

Q8: 能不能在MeasureOverride或者ArrangeOverride当中调用孩子的孩子,也就是孙子的Measure或者Arrange方法?

目前来看,WPF/SL是允许这样做的。但是,通过研究Layout系统之后,发现这个系统是非常庞大和复杂,另外,Arrange的时候, 孩子的位置总是基于自己的父,另外,内容会发一些基于父的事件,比如,ChildDesiredSizeChanged之类的事件。因此,个人觉得,最好不要这样做,谁也保证不了这样的做法是否不会出问题,无疑中也增加了代码的可读性。

影响WPF Layout的属性总结以及跟Silverlight的不同之处

 

WPF

Silverlight

Measure过程

Margin,MinWidth,Width 
MaxWidth,MinHeight,Height, 
MaxHeight,UseLayoutRounding, 
LayoutTransform

Margin,MinWidth,Width, 
MaxWidth,MinHeight,Height, 
MaxHeight,UseLayoutRounding

Arrange过程

Margin,MinWidth,Width 
MaxWidth,MinHeight,Height, 
MaxHeight,UseLayoutRounding, 
LayoutTransform,RenderTransform,RenderTransformOrigin 
HorizontalAlignment,VeritcalAlignment

Margin,MinWidth,Width 
MaxWidth,MinHeight,Height, 
MaxHeight,UseLayoutRounding, 
RenderTransform,RenderTransformOrigin 
HorizontalAlignment,VeritcalAlignment,Clip

原文地址:http://www.cnblogs.com/powertoolsteam/archive/2011/01/11/1932923.html

WPF/Silverlight Layout 系统概述——Arrange(转)的更多相关文章

  1. WPF/Silverlight Layout 系统概述——Measure(转)

    前言 在WPF/Silverlight当中,如果已经存在的Element无法满足你特殊的需求,你可能想自定义Element,那么就有可能会面临重写MeasureOverride和ArrangeOver ...

  2. WPF Layout 系统概述——Arrange

    原文:WPF Layout 系统概述--Arrange Arrange过程概述 普通基类属性对Arrange过程的影响 我们知道Measure过程是在确定DesiredSize的大小,以便Arrang ...

  3. WPF Layout 系统概述——Measure

    原文:WPF Layout 系统概述--Measure 前言 在WPF/Silverlight当中,如果已经存在的Element无法满足你特殊的需求,你可能想自定义Element,那么就有可能会面临重 ...

  4. XData -–无需开发、基于配置的数据库RESTful服务,可作为移动App和ExtJS、WPF/Silverlight、Ajax等应用的服务端

    XData -–无需开发.基于配置的数据库RESTful服务,可作为移动App和ExtJS.WPF/Silverlight.Ajax等应用的服务端   源起一个App项目,Web服务器就一台,已经装了 ...

  5. WPF/Silverlight HierarchicalDataTemplate 模版的使用(转)

    上一篇 对Wpf/Silverlight Template 进行了总结,本篇继续上一篇,主要是介绍 HierarchicalDataTemplate 的使用方法.HierarchicalDataTem ...

  6. WPF/Silverlight Template使用及总结(转)

    WPF/Silverlight 中的控件都有Style和Template两种属性.前者解释为样式,是用来改变控件原有属性的,比如 Button 控件的(Width,Height,Background ...

  7. Mvvm Light Toolkit for WPF/Silverlight系列之搭建mvvmlight开发框架

    Mvvm Light Toolkit for WPF/Silverlight系列之搭建mvvmlight开发框架   本章节,我将通过示例介绍如何搭建mvvmlight开发环境.示例中的我会针对wpf ...

  8. WPF/Silverlight深度解决方案:(一)解锁被Storyboard束缚的关联属性

    原文 WPF/Silverlight深度解决方案:(一)解锁被Storyboard束缚的关联属性 如果您在使用WPF/Silverlight进行相关动画开发中使用了Storyboard,并对关联属性进 ...

  9. WPF/Silverlight中图形的平移,缩放,旋转,倾斜变换演示

    原文:WPF/Silverlight中图形的平移,缩放,旋转,倾斜变换演示 为方便描述, 这里仅以正方形来做演示, 其他图形从略. 运行时效果图:XAML代码:// Transform.XAML< ...

随机推荐

  1. Django 1.6 最佳实践: 如何设置和使用 Log(转)

    原文: http://www.weiguda.com/blog/37/ 任何参与过高要求的大型项目的编程人员都明白设置适当的log等级, 创建不同的logger, 记录重要事件的重要性. 正确的设置和 ...

  2. Codeforces Round #367 (Div. 2) D. Vasiliy's Multiset (0/1-Trie树)

    Vasiliy's Multiset 题目链接: http://codeforces.com/contest/706/problem/D Description Author has gone out ...

  3. 微软IOC容器Unity简单代码示例3-基于约定的自动注册机制

    @(编程) [TOC] Unity在3.0之后,支持基于约定的自动注册机制Registration By Convention,本文简单介绍如何配置. 1. 通过Nuget下载Unity 版本号如下: ...

  4. ado通用操作数据单元

    DELPHI开发2层C/S数据库应用程序,许多人通过ADOQUERY或ADOTABLE直接操作数据库,其实这种方法虽然最为直接,但有其缺点:如果以后要将程序升级为3层C/S会非常困难.而通过像下面的通 ...

  5. MFC中消息响应机制

    由于视类窗口始终覆盖在框架类窗口之上,因此所有操作,包括鼠标单击.鼠标移动等操作都只能由视类窗口捕获.一个MFC消息响应函数在程序中有三处相关信息:函数原型.函数实现和以及用来关联消息和消息响应函数的 ...

  6. How Tomcat Works(十二)

    tomcat容器通过一个称为Session管理器的组件来管理建立的Session对象,该组件由org.apache.catalina.Manager接口表示:Session管理器必须与一个Contex ...

  7. JavaScript一些不常用的写法

    如何写JavaScript才能逼格更高呢?怎样才能组织JavaScript才能让别人一眼看出你不简单呢?是否很期待别人在看完你的代码之后感叹一句“原来还可以这样写”呢?下面列出一些在JavaScrip ...

  8. Find mac address

    Windows Method 1: Using the Command Prompt 1 Click on the Start button.   2 Type cmd in the search b ...

  9. 无责任Windows Azure SDK .NET开发入门篇二[使用Azure AD 进行身份验证]

    二.使用Azure AD进行身份验证 之所以将Azure AD 作为开始,是应为基本上我们所有应用都需要进行安全管理.Azure Active Directory (Azure AD) 通过以下方式简 ...

  10. HDU 2066 一个人的旅行 - from lanshui_Yang

    Problem Description 虽然草儿是个路痴(就是在杭电待了一年多,居然还会在校园里迷路的人,汗~),但是草儿仍然很喜欢旅行,因为在旅途中 会遇见很多人(白马王子,^0^),很多事,还能丰 ...