本文通过一个实例程序,比较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 元素来处理这一问题。