在上一篇中講述了使用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