在上一篇中讲述了使用VS2010开发Silverlight的一些基础知识,并且讲述了Silverlight的部署和代码安全知识,这一篇主要是讲述如何在Silverlight中摆放界面元素。
记得早年前我还在学习Java的时候,当时有两种开发Java SE的方法,一种是使用JCreator或者JBuilder之类的IDE开发(现在这二者都几乎没人用了,流行的是Eclipse或者NetBeans);一种是使用Visual J++开发。使用前一种方法开发的Java程序可以多种操作系统平台上运行,不过界面布局比较麻烦,什么CardLayout、FlowLayout、BorderLayout、GridBagLayout、GridLayout等等,开发一个复杂的界面需要开发人员对各种布局类都有所了解;使用Visual J++开发的话可以使用XY坐标来定位元素,相对来说容易多了,不过这种开发的Java软件并不是严格意义上的Java软件,它只能在Windows平台上运行。Java从出现到现在,在Java EE和Java ME上都相对比较成功,而唯独在Java SE上表现不佳,不知道跟它难以使用的界面布局有关系。
布局概述
在上一篇提到了XAML语言,它适用于在WPF和Silverlight中进行界面布局的标记语言,它是一种有特定要求的XML语言,从某种意义上来说,我觉得它和XHTML走得更近一些:首先它们都是有特定格式的XML语言,其次它们都是用于界面布局。除此之外,在XAML语言中还有一个特点,那就是每一个元素都代表着一个Silverlight中的类,并且在XAML中只能有一个顶级元素。因此在进行WPF和Silverlight开发时不能绕开的一个问题就是界面布局,在Silverlight中常见的界面布局类有Canvas、Grid和StackPanel。
Canvas、Grid和StackPanel其实都是继承自System.Windows.Controls.Panel的类,它们的继承层次关系如下图:
Panel类有如下比较常见的属性:
Background:用于填充 Panel 的边框之间的区域的 Brush。
Children:此 Panel 的子元素的 UIElementCollection。
Height:元素的建议高度。
HorizontalAlignment:在父元素(如面板或项控件)中构成此元素时应用于此元素的水平对齐特征。
MaxHeight:元素的最大高度约束,MaxHeight的默认值是PositiveInfinity(正无穷大)。
MaxWidth:元素的最大宽度约束,MaxWidth的默认值是PositiveInfinity(正无穷大)。
MinHeight:元素的最小高度约束,MinHeight的默认值分别是Auto(自动调整)。
MinWidth:元素的最小宽度约束,MinWidth的默认值分别是Auto(自动调整)。
VerticalAlignment:在父元素(如面板或项控件)中组合此元素时应用于此元素的垂直对齐特征。
Width:元素的宽度。
可以看出在这里存在着Height、MaxHeigh、MinHeight及Width、MaxWidth、MinWidth这么两组与高度和宽度相关的属性,这的确让初学的人有些模糊。这些值之间存在着什么样的关系呢?拿Width、MaxWidth、MinWidth来说,它们存在的关系如下:如果这三个值之间存在冲突,则应用程序确定宽度的实际顺序是:首先必须采用 MinWidth;然后采用 MaxWidth;最后,如果这些值中的每个值都在限制之内,则采用 Width。为什么对于Width或者Height会出现这么三个属性呢?这是跟编程有一定的关系,假如我们在一个布局容器中水平放置了三个按钮,每个按钮的宽度是60像素,即使不考虑这三个按钮之间的间隙显示这三个按钮的宽度至少需要180像素,在默认情况下Width、MaxWidth、MinWidth的默认值分别是Auto(自动调整)、PositiveInfinity(正无穷大)、0.0,这样一来按照上面的规则会采取自动调整的方式。
StackPanel布局用法
StackPane是上面提到的几种布局中最简单的一种布局方式,它在一行或者一列中显示所有的子控件,默认情况下它是在一列中显示所有元素的,不过可以通过设置它的Orientation 属性为Horizontal以指示在一行中显示所有元素。
下面是一个使用StackPanel的简单例子:
<navigation:Page x:Class="SilverlightDemo1.StackPanelDemo" 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" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="640" d:DesignHeight="480" Title="StackPanelDemo Page"> <StackPanel Height="100" Name="stackPanel1" Width="200" Background="Yellow"> <Button Content="按钮一" Height="23" Name="button1" Width="100" /> <Button Content="按钮二" Height="23" Name="button2" Width="200" /> <Button Content="按钮三" Height="23" Name="button3" Width="400" /> </StackPanel> </navigation:Page>
这个Page的显示效果如下:
在上面的代码中我们设置StackPanel的Width为200,没有设置MaxWidth、MinWidth的值,最终实际显示宽度为200,因为此时MaxWidth和MinWidth都采用了默认值,因为这这三个值有冲突但是都在限制(没有找到具体对限制的定义,周公推测为MinWidth≤Width≤MaxWidth,如果您觉得周公的推测不正确,请告知以免误导大家,谢谢)之内,所以最终实际宽度为200。
如果设置StackPanel的Width、MaxWidth、MinWidth分别为200、400、100,最终实际显示宽度仍为200,原因同上,如下图所示:
如果设置StackPanel的Width、MaxWidth、MinWidth分别为200、100、100,最终实际显示宽度为100,这里MaxWidth和MinWidth都是100,而Width却是200不在限制之内,所以最终显示宽度为MinWidth设置的宽度,如下图所示:
如果设置StackPanel的Width、MaxWidth、MinWidth分别为200、400、500,最终实际显示宽度为500,这里MaxWidth和MinWidth分别是400和500,而Width却是200不在限制之内,所以最终显示宽度也为MinWidth设置的宽度,如下图所示:
Grid布局用法
Grid布局是Silverlight一种比较复杂的布局,它有点像我们HTML中的Table元素,将空间划分为行和列组成的单元格,在每个单元格中可以放置其它元素,下面是一个使用Grid的例子:
<navigation:Page x:Class="SilverlightDemo1.GridDemo1" 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" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="400" d:DesignHeight="300" Title="GridDemo1 Page"> <Grid x:Name="LayoutRoot" Background="Pink"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="200" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="100" /> <ColumnDefinition Width="100" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Button Content="按钮一" Height="23" HorizontalAlignment="Left" Name="button1" VerticalAlignment="Center" Width="75" Grid.Column="0" Grid.Row="0" /> <Button Content="按钮二" Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Center" Name="button2" VerticalAlignment="Top" Width="75" /> <TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Center" Name="textBox1" VerticalAlignment="Center" Width="80" Text="文本框" /> </Grid> </navigation:Page>
它的显示效果如下:
当然Grid也可以像HTML中的Table一样跨行或者跨列,这需要通过设置控件的RowSpan或者ColumnSpan属性,下面就是一个例子:
<navigation:Page x:Class="SilverlightDemo1.GridDemo1" 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" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="400" d:DesignHeight="300" Title="GridDemo1 Page"> <Grid x:Name="LayoutRoot" Background="Pink"> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="200" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="100" /> <ColumnDefinition Width="100" /> <ColumnDefinition Width="100" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Button Content="按钮一" Height="220" HorizontalAlignment="Left" Name="button1" Width="75" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" /> <Button Content="按钮二" Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Center" Name="button2" VerticalAlignment="Top" Width="75" /> <TextBox Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="2" Height="23" Name="textBox1" VerticalAlignment="Center" Width="80" Text="文本框" /> <Button Content="按钮三" Grid.Column="2" Height="23" HorizontalAlignment="Left" Name="button3" VerticalAlignment="Top" Width="75" /> <Button Content="按钮四" Grid.Column="3" Grid.Row="1" Height="23" HorizontalAlignment="Left" Name="button4" VerticalAlignment="Top" Width="75" /> </Grid> </navigation:Page>
它的显示效果如下:
Canvas布局用法
相比Grid和Grid的布局方式来说,Canvas提供了另外一种途径来布置我们的控件,它采用了我们比较熟悉的利用坐标的方式的,在使用Canvas布局时可以设置每个控件Top和Left属性,也就是设置控件距离它所在的容器的距离,下面就是一个例子:
<navigation:Page x:Class="SilverlightDemo1.CanvasDemo1" 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" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="640" d:DesignHeight="480" Title="CanvasDemo1 Page"> <Canvas Height="240" Name="canvas1" Width="300" Background="Teal"> <Button Canvas.Left="40" Canvas.Top="161" Content="登录" Height="23" Name="button1" Width="75" /> <TextBlock Canvas.Left="40" Canvas.Top="56" Height="23" Name="textBlock1" Text="用户名" /> <TextBlock Canvas.Left="40" Canvas.Top="102" Height="23" Name="textBlock2" Text="密码" /> <Button Canvas.Left="183" Canvas.Top="161" Content="取消" Height="23" Name="button2" Width="75" /> <TextBox Canvas.Left="138" Canvas.Top="56" Height="23" Name="textBox1" Width="120" /> <PasswordBox Canvas.Left="138" Canvas.Top="102" Height="23" Name="passwordBox1" Width="120" /> </Canvas> </navigation:Page>
它的显示效果如下:
在代码中我们对用户名所对应的文本框的设置是:
<TextBox Canvas.Left="138" Canvas.Top="56" Height="23" Name="textBox1" Width="120" />
于是就会在距离Canvas顶部56、左边138处显示一个高度为23、宽度为120的文本框。
布局的综合使用
虽然在XAML中只能有一个顶级元素,但是这并不意味着在一个界面中只使用一种界面布局,我们完全可以在外层布局中嵌套内层布局,就像我们在HTML的Table中再次嵌套Table一样,下面是一个简单的例子:
<navigation:Page x:Class="SilverlightDemo1.Graphics" 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" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" d:DesignWidth="800" d:DesignHeight="600" Title="Chapter10 Page"> <StackPanel Width="800" Height="600" Orientation="Vertical"> <Canvas Width="800" Height="200" Background="White"> <Canvas.Resources> <Storyboard x:Name="myStroryboard"> <DoubleAnimation Storyboard.TargetName="myTransform" Storyboard.TargetProperty="Angle" From="0" To="360" Duration="0:0:5" RepeatBehavior="Forever"/> </Storyboard> </Canvas.Resources> <Image Canvas.Left="50" Canvas.Top="50" Height="100" Name="image01" Stretch="Fill" Width="100" Source="image/15.jpg" MouseEnter="Image_MouseEnter" MouseLeave="Image_MouseLeave"> <Image.RenderTransform> <RotateTransform x:Name="myTransform" Angle="15" CenterX="50" CenterY="50"></RotateTransform> </Image.RenderTransform> </Image> <Image Canvas.Left="350" Canvas.Top="0" Height="100" Name="image02" Stretch="Fill" Width="100" Source="image/15.jpg"> </Image> <Image Canvas.Left="350" Canvas.Top="0" Height="100" Name="image03" Stretch="Fill" Width="100" Source="image/15.jpg" Opacity="0.8"> <Image.RenderTransform> <TransformGroup> <ScaleTransform ScaleY="-0.75"></ScaleTransform> <TranslateTransform Y="180" X="30"></TranslateTransform> <SkewTransform AngleX="-15"></SkewTransform> </TransformGroup> </Image.RenderTransform> <Image.OpacityMask> <LinearGradientBrush StartPoint="0.5,0.0" EndPoint="0.5,1.0"> <GradientStop Offset="0.0" Color="#00000000"></GradientStop> <GradientStop Offset="1.0" Color="#FF000000"></GradientStop> </LinearGradientBrush> </Image.OpacityMask> </Image> </Canvas> <Canvas Width="800" Height="200"> <Image Canvas.Left="100" Canvas.Top="10" Height="100" Name="image31" Stretch="Fill" Width="200" Source="image/14.jpg" /> <Image Canvas.Left="100" Canvas.Top="10" Height="100" Name="image32" Stretch="Fill" Width="200" Source="image/14.jpg"> <Image.RenderTransform> <RotateTransform Angle="15" CenterX="0" CenterY="0"></RotateTransform> </Image.RenderTransform> </Image> <Image Canvas.Left="100" Canvas.Top="10" Height="100" Name="image33" Stretch="Fill" Width="200" Source="image/14.jpg"> <Image.RenderTransform> <RotateTransform Angle="30" CenterX="50" CenterY="300"></RotateTransform> </Image.RenderTransform> </Image> <Image Canvas.Left="100" Canvas.Top="10" Height="100" Name="image34" Stretch="Fill" Width="200" Source="image/14.jpg"> <Image.RenderTransform> <RotateTransform Angle="45" CenterX="0" CenterY="50"></RotateTransform> </Image.RenderTransform> </Image> </Canvas> <Canvas Width="800" Height="200"> <Image Canvas.Left="100" Canvas.Top="10" Height="100" Name="image1" Stretch="Fill" Width="200" Source="image/14.jpg" /> <Image Canvas.Left="100" Canvas.Top="10" Height="100" Name="image2" Stretch="Fill" Width="200" Source="image/14.jpg"> <Image.RenderTransform> <RotateTransform Angle="15"></RotateTransform> </Image.RenderTransform> </Image> <Image Canvas.Left="100" Canvas.Top="10" Height="100" Name="image3" Stretch="Fill" Width="200" Source="image/14.jpg"> <Image.RenderTransform> <RotateTransform Angle="30"></RotateTransform> </Image.RenderTransform> </Image> <Image Canvas.Left="100" Canvas.Top="10" Height="100" Name="image4" Stretch="Fill" Width="200" Source="image/14.jpg"> <Image.RenderTransform> <RotateTransform Angle="40"></RotateTransform> </Image.RenderTransform> </Image> </Canvas> </StackPanel> </navigation:Page>
它的显示效果如下:
总结:本篇主要讲述了Silverlight中几种常见的布局:StackPanel可以将控件按行或者按列布局,这是一种比较简单的布局方式;Grid可以采用类似于HTML中Table的方式布局,并且可以设置控件跨行或者跨列摆放;Canvas控件采用类似于坐标定位的方式对控件进行布局。还有一些布局在本篇中没有讲述,读者朋友可以在学习时借鉴这些知识来学习,其实利用这些布局已经足够实现复杂的界面了。
下一篇将讲述常用控件的学习。
周公(zhoufoxcn)
2010-10-11