本文通过一个实例程序,比较Silverlight中应用面板控件Canvas,StackPanel,DockPanel,WrapPanel,Grid,VirtualizingStackPanel ,ViewBox来布局时,它们产生的效果异同。
一、不同面板的布局效果
二、源代码
xmlns = " http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml "
xmlns:d = " http://schemas.microsoft.com/expression/blend/2008 "
xmlns:mc = " http://schemas.openxmlformats.org/markup-compatibility/2006 "
mc:Ignorable = " d "
d:DesignHeight = " 300 " d:DesignWidth = " 800 " >
< Grid x:Name = " LayoutRoot " Background = " White " ShowGridLines = " True " >
< Grid.RowDefinitions >
< RowDefinition Height = " 50 " />
< RowDefinition Height = " 50* " />
</ Grid.RowDefinitions >
< Grid.ColumnDefinitions >
< ColumnDefinition Width = " 160* " />
< ColumnDefinition Width = " 160* " />
< ColumnDefinition Width = " 160* " />
< ColumnDefinition Width = " 160* " />
< ColumnDefinition Width = " 160* " />
</ Grid.ColumnDefinitions >
< sdk:Label Grid.Row = " 0 " Grid.Column = " 0 " HorizontalContentAlignment = " Center " Content = " Canvas " />
< Canvas Grid.Row = " 1 " Grid.Column = " 0 " >
< Button Background = " Red " > Left = 0 , Top = 0 </ Button >
< Button Canvas.Left = " 18 " Canvas.Top = " 18 "
Background = " Orange " > Left = 18 , Top = 18 </ Button >
</ Canvas >
< sdk:Label Grid.Row = " 0 " Grid.Column = " 1 " HorizontalContentAlignment = " Center " Content = " StackPanel " />
< StackPanel Grid.Row = " 1 " Grid.Column = " 1 " FlowDirection = " LeftToRight " VerticalAlignment = " Center " >
< Button Background = " Red " > 1 </ Button >
< Button Background = " Orange " > 2 </ Button >
< Button Background = " Yellow " > 3 </ Button >
< Button Background = " Lime " > 4 </ Button >
< Button Background = " Aqua " > 5 </ Button >
</ StackPanel >
< sdk:Label Grid.Row = " 0 " Grid.Column = " 2 " HorizontalContentAlignment = " Center " Content = " DockPanel " />
< toolkit:DockPanel Grid.Row = " 1 " Grid.Column = " 2 " VerticalAlignment = " Center " >
< Button Background = " Red " > 1 </ Button >
< Button Background = " Orange " > 2 </ Button >
< Button Background = " Yellow " > 3 </ Button >
< Button Background = " Lime " > 4 </ Button >
< Button Background = " Aqua " > 5 </ Button >
</ toolkit:DockPanel >
< sdk:Label Grid.Row = " 0 " Grid.Column = " 3 " HorizontalContentAlignment = " Center " Content = " WrapPanel " />
< toolkit:WrapPanel Grid.Row = " 1 " Grid.Column = " 3 " VerticalAlignment = " Center " >
< Button Background = " Red " > 1 </ Button >
< Button Background = " Orange " > 2 </ Button >
< Button Background = " Yellow " > 3 </ Button >
< Button Background = " Lime " > 4 </ Button >
< Button Background = " Aqua " > 5 </ Button >
</ toolkit:WrapPanel >
< sdk:Label Grid.Row = " 0 " Grid.Column = " 4 " HorizontalContentAlignment = " Center " Content = " Grid " />
< Grid Grid.Row = " 1 " Grid.Column = " 4 " >
< Grid.RowDefinitions >
< RowDefinition Height = " 20* " />
< RowDefinition Height = " 20* " />
< RowDefinition Height = " 20* " />
< RowDefinition Height = " 20* " />
</ Grid.RowDefinitions >
< Grid.ColumnDefinitions >
< ColumnDefinition Width = " 20* " />
< ColumnDefinition Width = " 20* " />
< ColumnDefinition Width = " 20* " />
< ColumnDefinition Width = " 20* " />
< ColumnDefinition Width = " 20* " />
</ Grid.ColumnDefinitions >
< Button Grid.Row = " 1 " Grid.Column = " 1 " Grid.RowSpan = " 2 " Grid.ColumnSpan = " 3 " Background = " Green " BorderBrush = " Red " BorderThickness = " 5 " ></ Button >
</ Grid >
< sdk:GridSplitter Grid.Row = " 1 " Grid.Column = " 0 " Width = " 10 " Background = " Lime " />
< sdk:GridSplitter Grid.Row = " 1 " Grid.Column = " 1 " Width = " 10 " Background = " Lime " />
< sdk:GridSplitter Grid.Row = " 1 " Grid.Column = " 2 " Width = " 10 " Background = " Lime " />
< sdk:GridSplitter Grid.Row = " 1 " Grid.Column = " 3 " Width = " 10 " Background = " Lime " />
</ Grid >
</ UserControl >
三、通过分析它们的特点,得出它们适合应用的场合。
不同布局面板的特点:
1、Canvas
Canvas 使您可以进行 (x,y) 定位,这与 GDI 和 GDI+ 目前提供的功能类似。您还可以使用附加的属性Left,Top来控制
项的位置。
2、DockPanel
DockPanel使您在停靠项时无需担心它们的确切 (x,y) 位置。
3、StackPanel 提供一个从左至右(水平)或从上至下(垂直)放置内容的堆栈模型。
StackPanel的默认排列方式为Vertical(垂直),但是如果你想指定为Horizontal(水平),
那么你可以使用属性Orientation="Horizontal"。
4、WrapPanel
WrapPanel用于在水平方向或垂直方向顺序显示子元素,当到达边缘时,则本行不足以显示的子元素对象自动换到下一行顺序显示。
5、Grid
Grid 提供一个允许进行行/网格定位的模型:
可用 SharedSizeGroup 属性为多个对象指定共享的大小。
例如,可使用该属性来指定两个按钮具有相同的宽度(即使它们通常具有不同的大小范围)。
< Grid LayoutTransform = " scale 2 " ShowGridLines = " true " IsSharedSizeScope = " true " Height = " 80 " >
< ColumnDefinition Width = " 30 " />
< ColumnDefinition Width = " Auto " SharedSizeGroup = " Buttons " />
< ColumnDefinition Width = " Auto " SharedSizeGroup = " Buttons " />
< RowDefinition />
< Button Grid.Column = " 1 " Margin = " 10 " > OK </ Button >
< Button Grid.Column = " 2 " Margin = " 10 " > A very long cancel button </ Button >
</ Grid >
可以指定元素使用Grid 的RowSpan,ColumnSpan来设置元素占用Grid的表格数量。效果如上图右边效果显示。
< Grid.RowDefinitions >
< RowDefinition Height = " 20* " />
< RowDefinition Height = " 20* " />
< RowDefinition Height = " 20* " />
< RowDefinition Height = " 20* " />
</ Grid.RowDefinitions >
< Grid.ColumnDefinitions >
< ColumnDefinition Width = " 20* " />
< ColumnDefinition Width = " 20* " />
< ColumnDefinition Width = " 20* " />
< ColumnDefinition Width = " 20* " />
< ColumnDefinition Width = " 20* " />
</ Grid.ColumnDefinitions >
< Button Grid.Row = " 1 " Grid.Column = " 1 " Grid.RowSpan = " 2 " Grid.ColumnSpan = " 3 " Background = " Green " BorderBrush = " Red " BorderThickness = " 5 " >
< Button.Content >
< Grid >
< Grid.RowDefinitions >
< RowDefinition ></ RowDefinition >
< RowDefinition ></ RowDefinition >
</ Grid.RowDefinitions >
< Image Source = " Images/note.png " Grid.Row = " 0 " >
</ Image >
< TextBlock Text = " FRJ提醒 " Grid.Row = " 1 " VerticalAlignment = " Center " HorizontalAlignment = " Center " >
</ TextBlock >
</ Grid >
</ Button.Content >
</ Button >
6、ViewBox
ViewBox 具有约束内容的大小以适应父面板的相反效果。这就提供了一种自动缩放效果。
以下示例中的文字,将根据窗口的大小,自动变大与缩小。
<Grid x:Name="LayoutRoot" Background="White">
<Border>
<Viewbox>
<TextBlock FontFamily="Global User Interface"> 人之初,性本善,性相近,习相远。苟不教,性乃迁,教之道,贵以专。</TextBlock>
</Viewbox>
</Border>
</Grid>
7、VirtualizingStackPanel
将内容排列和虚拟化在一行上,方向为水平或垂直。
VirtualizingStackPanel在普通StackPanel的基础上,提供了”虚拟化”的功能。那什么是”虚拟化”呢?先来看一个场景,如果你有一堆基于项的数据,并且你只需要让其中一部分显示在界面上,但是如果你针对里面所有的数据创建界面元素,并显示你需要的元素,那么这将是及其耗时的。”虚拟化”就是用于解决这个问题,它可以只创建你需要显示的数据的界面元素。即 “虚拟化”是指一种技术,通过该技术,可根据屏幕上所显示的项来从大量数据项中生成user interface (UI) 元素的子集。
VirtualizingStackPanel中的项只有在数据绑定的状态下,才提供”虚拟化”,否则和普通的StackPanel没有区别。
即仅当 StackPanel 中包含的项控件创建自己的项容器时,才会在该面板中发生虚拟化。 可以使用数据绑定来确保发生这一过程。 如果创建项容器并将其添加到项控件中,则与 StackPanel 相比,VirtualizingStackPanel 不能提供任何性能优势。
应用示例:
http://msdn.microsoft.com/zh-cn/library/system.windows.controls.virtualizingstackpanel.aspx
下面的示例演示如何使用Extensible Application Markup Language (XAML) 绑定到 XML 数据源,并虚拟化 ListBox 元素中显示的项。 注意:IsVirtualizing 附加属性显式设置为 true。
xmlns:x = " http://schemas.microsoft.com/winfx/2006/xaml "
WindowTitle = " VirtualizingStackPanel Sample "
Height = " 150 "
VerticalAlignment = " Top " >
< Page.Resources >
< XmlDataProvider x:Key = " Leagues " Source = " Leagues.xml " XPath = " Leagues/League " />
< DataTemplate x:Key = " NameDataStyle " >
< TextBlock Text = " {Binding XPath=@name} " FontFamily = " Arial " FontSize = " 12 " Foreground = " Black " />
</ DataTemplate >
</ Page.Resources >
< Border HorizontalAlignment = " Left "
VerticalAlignment = " Top "
BorderBrush = " Black "
BorderThickness = " 2 " >
< ScrollViewer >
< StackPanel DataContext = " {Binding Source={StaticResource Leagues}} " >
< TextBlock Text = " {Binding XPath=@name} " FontFamily = " Arial " FontSize = " 18 " Foreground = " Black " />
< ListBox VirtualizingStackPanel.IsVirtualizing = " True "
ItemsSource = " {Binding XPath=Team} "
ItemTemplate = " {DynamicResource NameDataStyle} " />
</ StackPanel >
</ ScrollViewer >
</ Border >
</ Page >
下面的示例创建一个 ListBox,并将 VirtualizingStackPanel.VirtualizationMode 附加属性设置为 Recycling。
< StackPanel.Resources >
< src:LotsOfItems x:Key = " data " />
</ StackPanel.Resources >
< ListBox Height = " 150 " ItemsSource = " {StaticResource data} "
VirtualizingStackPanel.VirtualizationMode = " Recycling " />
</ StackPanel >
下面的示例演示上例中所使用的数据。
{
public LotsOfItems()
{
for ( int i = 0 ; i < 1000 ; ++ i)
{
Add( " item " + i.ToString());
}
}
}
布局是任何用户界面子系统的基础服务之一,它涉及如何确定元素在窗口中的位置。设计 Windows Presentation Foundation 布局系统的目的是为灵活的可扩展模型提供支持,该模型针对内容进行优化,并且能够正确地处理数据、样式和控件。
传统的应用程序平台(如 Win32)几乎没有布局的概念:控件放置在画布上的 (x,y) 坐标系中,并且开发人员需要手动提供对确定任何元素的原点和尺寸的支持(考虑窗口大小调整和显示器 DPI 设置)。另一方面,Windows Presentation Foundation 提供多种适合于内容并且在窗口内管理控件和项目位置的布局实现。
在 Windows Presentation Foundation 中,几乎可以使用任何元素作为其他元素的宿主。例如,Button 可按如下方式包含图像:
< Button.Content >
< Grid >
< Grid.RowDefinitions >
< RowDefinition ></ RowDefinition >
< RowDefinition ></ RowDefinition >
</ Grid.RowDefinitions >
< Image Source = " Images/note.png " Grid.Row = " 0 " >
</ Image >
< TextBlock Text = " FRJ提醒 " Grid.Row = " 1 " VerticalAlignment = " Center " HorizontalAlignment = " Center " >
</ TextBlock >
</ Grid >
</ Button.Content >
</ Button >
面板负责其子元素的布局。它们定义内容的大小和形状、对齐方式和边距。面板的子元素可以是诸如 TextBox 或 Button 这样的元素,也可以是另一个面板。
例如,DockPanel 可以包含 StackPanel 项调整大小以适应它们所包含的内容 — 这是一个重要的概念,因为它确保了应用程序的本地化不需要重新校准应用程序布局(因为不同语言的同一句子 — 例如“Auf Wiedersehen”与“Goodbye”— 具有不同的文本长度)。可用 HorizontalAlignment/VerticalAlignment 属性应用对齐方式,并且使用 Margin 属性指定边距。ScrollViewer 提供子元素内容的滚动视图;如果内容溢出可用的空间,则会显示一个滚动条并允许用户在内容区域周围移动。
例如:
<Border >
<ScrollViewer>
<TextBlock FontSize="20" TextWrapping="Wrap" FontFamily="Global User Interface">
人之初,性本善,性相近,习相远。苟不教,性乃迁,教之道,贵以专。
昔孟母,择邻处,子不学,断机杼。窦燕山,有义方,教五子,名俱扬。
养不教,父之过,教不严,师之惰。子不学,非所宜,幼不学,老何为?
玉不琢,不成器,人不学,不知义。为人子,方少时,亲师友,习礼仪。
香九龄,能温席,孝于亲,所当执。融四岁,能让梨,悌于长,宜先知。
</TextBlock>
</ScrollViewer>
</Border>
布局协议是一个递归过程,它是某种形式的协商,目的是确保所有元素都基于其请求获得“公平”数量的空间。首先,将发生一个测量过程,其中,父元素询问子元素希望自己多大,而子元素用 DesiredSize 进行响应。然后,将发生排列过程,其中,父元素以 ActualSize 属性的形式告诉子元素它将有多大。在进行计算时,特殊值 Double.PositiveInfinity 意味着“适合于内容”。可通过重写面板的 MeasureOverride 和 ArrangeOverride 元素来处理这一问题。