Silverlight 布局系统
Silverlight
本主题介绍 Silverlight 布局系统。在构造外观醒目、性能优良的用户界面时,了解如何在 HTML 页中定位插件以及在插件内如何和何时进行布局计算是非常重要的。
本主题包括下列各节。
Silverlight 插件的位置和尺寸
布局系统
元素边界框
测量和排列子元素
Panel 元素和自定义布局行为
窗口大小调整
相关主题
Silverlight 插件的位置和尺寸
Silverlight 插件定义在其中显示基于 Silverlight 的应用程序的区域。可以将该插件嵌入到某一宿主 HTML 页中,并且可以将该插件以内联方式定位到 HTML 页显示的某个位置,也可以使该插件占据整个 HTML 页。因此,定位 Silverlight 对象时,有两个参考框架:
插件内:将 Silverlight 图面上的对象定位在该插件的边界框内。大多数布局概述都介绍了此类型的定位。
HTML 内:整个插件以及位于它里面的所有对象都受插件在 HTML 中的放置位置的限制。有关该插件的更多信息,请参见 使用 HTML 或 JavaScript 将 Silverlight 添加到网页。
布局系统
布局一词描述在基于 Silverlight 的应用程序中调整对象大小和定位对象的过程。要定位可视化对象,您必须将它们放置于 Panel 或其他容器对象中。父 Panel 定义了确定如何在屏幕上绘制 Panel 元素的 Children 集合成员的布局行为。这是一个计算密集型过程,即 Children 集合越大,执行的计算次数就越多。根据拥有该集合的 Panel 元素所定义的布局行为,还可能会增加复杂性。如果不需要较为复杂的 Panel(如 Grid),则可以使用构造相对简单的布局(如 Canvas),这种布局可产生更佳的性能。
每当子 UIElement 改变其位置时,布局系统就可能触发一个新的处理过程。因此,了解哪些事件会调用布局系统就很重要,因为不必要的调用可能导致应用程序性能变差。
简单地说,布局是一个递归系统,实现在屏幕上对元素进行大小调整、定位和绘制。布局系统为 Children 集合的每个成员完成两个处理过程:第一个是测量处理过程,第二个是排列处理过程。测量处理过程是确定每个子元素所需大小的过程。排列处理过程是最终确定每个子元素的大小和位置的过程。
说明:当指代元素布局时,说成是该元素的边界框的布局更为准确。有关更多信息,请参见本主题后面的元素边界框。
若要重写默认 Panel 布局行为,则每个类型的 Panel 均需要提供自己的 MeasureOverride 和 ArrangeOverride 方法,以实现自己特定的布局行为。不论何时调用布局系统,都会发生以下系列事件:
- 测量每个子 UIElement。
计算在 FrameworkElement 上定义的大小调整属性,例如 Width、 Height 和 Margin。
应用 Panel 特定的逻辑,例如堆栈 Orientation。
测量所有子元素后排列内容。
Children 集合绘制到屏幕。
如果其他 Children 添加到了集合中、 Children 的布局属性(如 Width 和 Height)发生了改变或调用了 UpdateLayout 方法,均会再次调用该过程。
下面的章节将更详尽地定义此过程及其调用方式。
元素边界框
基于 Silverlight 的应用程序中的所有元素周围都围绕着一个边界框,了解这一点非常重要。这有助于您理解布局系统的行为。当布局系统定位任何 FrameworkElement 时,实际上是在定位包含该元素的矩形或布局槽。
LayoutInformation 类将被公开,以返回元素的布局槽和可视化区域的几何边界。布局槽的大小是由系统通过计算可用屏幕空间、任何约束的大小、布局特定的属性(如边距和填充)及父 Panel 元素的个别行为来确定的。通过此数据,系统将能够计算给定的 Panel 的所有子元素的位置。牢记在父元素上定义的哪些大小调整特性(如 Border)会影响其子元素,这非常重要。
下图演示了父面板、其子元素以及包含该子元素的布局槽的尺寸。
可以看到,为该子元素分配的区域实际远远大于该子元素。由父容器决定每个子元素的布局槽大小。父容器分配的空间既可大于也可小于子元素请求的空间。可以通过调用 GetLayoutSlot 来获得布局槽的尺寸。然后,父容器根据在子元素上设置的对齐方式属性将子元素定位到其布局槽内。
下图演示了一个子元素,该子元素进行了旋转,并且现在超出了分配给它的布局槽。
在这种情况下,布局系统将裁剪子元素,并且仅显示布局槽内适合的元素部分。此可视化区域(轮廓为红色)称为布局裁剪,可以通过调用 GetLayoutClip 来获得其尺寸。请注意, GetLayoutClip 会返回一个 Geometry 对象,因此此可视化区域不一定是矩形。
当其他元素添加到父容器中时,元素周围的边界框可能会发生更改。根据所添加元素的类型和大小,边界框可能会缩小或放大。
测量和排列子元素
当元素需要呈现到屏幕时或者元素的大小发生更改时,将调用布局系统。布局的第一个处理过程是测量处理过程,将在该过程中确定每个子元素的所需大小。第二个处理过程是排列处理过程,将在该过程中确定每个子元素边界框的最终大小和位置。
测量 在测量处理过程中,布局系统会告诉 Panel 其 availableSize。这是父元素可用来为 Panel 设置其子元素的布局的区域。 Panel 将计算其每个子元素的本机大小属性,如 Clip 和 Visibility。
接下来,处理在每个子元素上定义的 FrameworkElement 属性。这些属性旨在描述基础 UIElement 的大小调整特性,例如其 Height、 Width、 Margin 和 Style。上述每个属性均可能改变显示元素所必需的空间。然后, Panel 将对其每个子元素调用 Measure 方法,以便传递相应子元素的可用大小。可用大小可以是子元素请求的大小,但父元素也可以根据它需要布局的元素数量及其 availableSize 选择限制子元素的大小。
说明:在 Height、 Width、 ActualHeight 和 ActualWidth 的属性之间存在着差异。例如, ActualHeight 属性是基于其他高度输入和布局系统的计算值。该值是由布局系统本身基于实际呈现处理过程设置的,因此可能会稍微小于作为输入更改基础的 Height 等属性的设置值。由于默认值为 0,因此必须设置 Height 和 Width。
由于 ActualHeight 是一个计算值,所以您应该知道,作为布局系统多个操作的结果,该值可能有多次或不断增加的报告的更改。布局系统可能正在计算子元素所需的测量空间、父元素的约束等。
测量处理过程的最终目标是让布局系统确定每个子元素的 DesiredSize,这是在调用 Measure 之后在内部发生的。将存储该值,并在排列过程期间使用它。
排列 在排列处理过程中,布局系统会告诉 Panel 可用于它及其子元素的 finalSize。在排列处理过程中,父 Panel 元素将计算子元素的 DesiredSize 以及可能会影响元素呈现大小的任何其他边距,并确定每个子元素的边界框。边界框决定子元素的布局槽的尺寸。然后,父 Panel 调用每个子元素的 Arrange 方法,以便传递用于设置子元素在面板中的原点及其高度和宽度的 Rect。
说明:所有布局都是相对于父元素的。如果将原点设置为 0,0,会将子元素放置在父面板的左上角,而不是放置在 Silverlight 插件的左上角。
布局系统执行偏移属性(例如边距和对齐方式)的最终计算,并将子元素放置在其布局槽内。子元素无需(且通常不会)填充整个分配空间。然后,控件返回到父 Panel,至此布局过程完成。
Panel 元素和自定义布局行为
Silverlight 包括 Panel 元素的派生套件,可以实现许多复杂的布局。常见方案(如堆栈元素)可以使用 StackPanel 元素方便地实现,而较为复杂的布局可以使用 Grid 来实现。
下表概括了可用的布局元素。
面板名称
说明
Canvas
定义一个区域,在此区域内,您可以使用相对于 Canvas 区域的坐标显式定位子元素。
Grid
定义由行和列组成的灵活网格区域。
StackPanel
将子元素排列成一行(可沿水平或垂直方向)。
上述每个布局容器都要考虑可能影响子元素排列方式的不同属性。下表显示了几个此类常见属性。
属性名
布局影响
Margin
在对象及其布局槽之间创建空间。
HorizontalAlignment 和 VerticalAlignment
将对象定位到其布局槽内。
Canvas.ZIndex
在两个对象重叠时将一个对象定位到另一个对象的顶部。
除了应用面板布局逻辑外,您还可以使用变换和动画来更改对象的位置。有关更多信息,请参见 变换和 动画。
在需要不可能使用任一预定义的 Panel 元素来实现的应用程序布局的情况下,自定义布局行为可以通过以下方式来实现:从 Panel 继承并使用 MeasureOverride 和 ArrangeOverride 方法重写默认度量和排列行为。有关更多信息,请参见 自定义面板。
窗口大小调整 尽管使用 Canvas 对对象进行绝对定位在某些情况下很有用,但是在大小可变的浏览器窗口中,这通常是一个很糟糕的策略。绝对定位不允许对象根据浏览器窗口的大小调整在页面上重新排列,对象保留在各自的指定像素位置。
StackPanel 和 Grid 允许内容重新排列。 Grid 对象是 Visual Studio Silverlight 模板使用的根元素。尽管在使用方面 Grid 对象比其他 Panel 对象要复杂一些,但是它支持内容的重新排列,并且其灵活性足以使您创建各种对象布局。
要获得最佳的窗口大小调整行为(除了使用 Panel 对象定位子对象外),通常来说最好的办法是将 DOM 的宽度和高度属性保留为 100%,并且在 XAML 文件的根元素或布局根元素中不包含任何宽度或高度声明。例如,在下面的 XAML 中,根元素或布局元素都没有定义宽度或高度。
Xaml代码
< UserControl x:Name = " RootElement " x:Class = " WindowResizingSample.Page " xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation " xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml " > < Grid x:Name = " LayoutRoot " Background = " White " > </ Grid > </ UserControl >