Evil 域

当Evil遇上先知

导航

Silverlight 学习笔记——布局

Posted on 2009-12-20 19:05  Saar  阅读(2247)  评论(0编辑  收藏  举报

 

对于程序开发来说,创建一个友好的用户界面,提供良好的用户体验相当重要。因此,如何有效组织、布局信息,对于像Sliverlight这样,要在不同浏览器如IEFirefox,不同显示器,不同操作系统的环境下运行的程序来说,尤为重要。

Silverlight提供了多种布局控件,例如StackPanelWrapPanelDockPanelGridCanvas等。通过混合运用这些不同的而已控件,开发人员可以构建出各种不各样的内容布局页面。

如图所示,在Silverlight中,所有这些而已控件,均继承自Panel类。而Panel类,扩展了UIElement类的BackgroundChildren属性。

 

 

我们可以把这些布局控件看作是布局的容器。从XAML可以看到,一个Silverlight窗口仅可以容纳一个元素,那么,要在页面上添加纷繁的控件以表达程序的内容,就需要通过容器,存放更多的其它控件。

让我们一个一个来熟悉Silverlight为我们提供的这些“容器”。

StackPanel

把控件件栈一样,以横向或纵向的方式堆积起来;

WrapPanel

以横向或纵向为主向,排布控件。当主方向已经不能再排布更多的控件时,在另一个方向上添加一个单位。例如,当以横向为主向时,横向已经排满控件时,控件就“流向”下一行——类似于ASP.NETListView。此控件在Silverlight Toolkit中提供。

DockPanel

靠边排布控件,此控件在Silverlight Toolkit中提供。

Grid

强大的布局控件,把界面划分成若干个行和列的隐形表格,把控件排布在不同的行与列中。Grid几乎是最常用的布局控件,以至于用Visual Studio创建Silverlight项目时就默认提供了一个Grid作为布局控件。

Canvas

允许控件以固定坐标出现在页面上的控件。用此控件布局最省力,但是,扩展能力也最差。

StackPanel

首先来看StackPanel。默认情况下,当我们往StackPanel里添加控件的时候,控件会垂直堆叠。例如:

 

        <StackPanel>
            
<Button Content="Button1" />
            
<Button Content="Button2" />
            
<Button Content="Button3" />
        
</StackPanel>    

 

 

就会生成一个垂直堆叠着三个按钮的界面:

 

此时,由于是垂直堆放,垂直方向上,按钮的高度是其可以显示的最小时,而水平方向上,则拉申至铺满整个容器。

有时候,我们需要水平方向上堆放控件,我们可以通过设置StackPanelOrientation属性来实现横向排布。

 

        <StackPanel Orientation="Horizontal">
            
<Button Content="Button1" />
            
<Button Content="Button2" />
            
<Button Content="Button3" />
        
</StackPanel>    

 

 

得到效果如下:

 

对于堆放于容器中的控件而言,也会有一些布局相关的属性会影响到最终的布局效果。

Alignment

当控件堆放在一个OrientationHorizontalStackPanel时,由于其水平方为控件堆叠方向,所以,HorizontalAlignment不会有效果。但是,如果用户设置了VerticalAlignment,控件垂直方向上主不再是拉申至整个容器,而会变成恰好能容纳文字的高度。例如:

 

代码
        <StackPanel Orientation="Horizontal">
            
<Button Content="Button1" VerticalAlignment="Top" />
            
<Button Content="Button2" VerticalAlignment="Bottom" />
            
<Button Content="Button3" VerticalAlignment="Center" />
        
</StackPanel>    

 

 

我们将得到三个按钮,从左到右依次为上对齐、下对齐和居中。

 

Margin

Margin控制控件的外边界。当一个控件的外边界为统一的数值的时候,我们可以只设置一个值;否则,按照左、上、右、下的顺序,以逗号隔开,依次设置。

例如,Margin="5",我们将得到上下左右均为5的外边界;而如果设置Margin="5,10,20,30",那么,左外边界为5,上外边界为10,右外边界为20,下外边界为30。这个与CSS中的上、右、下、左的顺序稍有差别。

来看一个具体的例子,我们给第二个按钮添加左右外边界:

 

代码
        <StackPanel Orientation="Horizontal">
            
<Button Content="Button1"/>
            
<Button Content="Button2" Margin="10,0,50,0"/>
            
<Button Content="Button3"/>
        
</StackPanel>    

 

 

得到结果如图:

 

当然,也可以对Button2设置上下外边距。

MinWidthMinHeight

这个比较容易理解,控件的最小宽度(高度),当界面空间小于控件的最小宽度(高度)时,控件将被裁掉一部分。

MaxWidthMaxHeight

设置控件的最大宽度(高度),当控件内容膨胀到超过控件的最大值时,控件内容将被截断;另外,即使设置了控件的HorizontalAlignmentVerticalAlignment为拉申,控件均不会超过其最大值。

WrapPanel

StackPanel适用于在同一个方向上堆放控件。WrapPanel同样也在一个方向上堆放控件,但是,如果这个方向上不够堆放了,那么,自动的执行换行操作。

由于WrapPanelDockPanel是通过Sliverlight Toolkit提供的,因此,要使用它们,我们需要首先安装Silverlight Tookit,可以通过http://www.codeplex.com/Silverlight下载Silverlight Toolkit并安装。

安装好了以后,我们需要通过添加控件窗口,把它添加到工具箱里,之后我们就可以添加一个WrapPanel了。

 

它跟StackPanel一样,有一个Orientation属性,用来设置排布方向,默认值为Horizontal。让我们把五个按钮放在一个WrapPanel里,然后,调整IE的大小,来看看会是什么效果。

 

代码
    <controlsToolkit:WrapPanel Name="WrapPanel1">
        
<Button Content="Button 1" />
        
<Button Content="Button 2" />
        
<Button Content="Button 3" />
        
<Button Content="Button 4" />
        
<Button Content="Button 5" />
    
</controlsToolkit:WrapPanel>    

 

 

效果如下,当横向宽度不够时,控件会在纵向添加一个行,继续横向的排布所有的控件。

 

DockPanel

DockPanel使内部的控件附着在自己的边缘。它跟WrapPanel一样,通过Silverlight Toolkit提供。

我们可以通过AttachProperty,设置控件附着在哪一个边缘,例如:

 

代码
    <controlsToolkit:DockPanel  Name="DockPanel1" LastChildFill="True">
        
<Button Content="Top Button" controlsToolkit:DockPanel.Dock="Top" />
        
<Button Content="Bottom Button" controlsToolkit:DockPanel.Dock="Bottom" />
        
<Button Content="Left Button" controlsToolkit:DockPanel.Dock="Left" />
        
<Button Content="Right Button" controlsToolkit:DockPanel.Dock="Right" />
        
<Button Content="Fill Button" />
    
</controlsToolkit:DockPanel>    

 

 

将会形成如下一个的布局:

 

有趣的是,DockPanel形成的布局与控件的顺序是有关系的。例如,把LeftButtonBottom Button顺序换一下,由于左按钮先Dock,会把除了上按钮以外的整个左侧占用,那么,底部Button就会被左按钮挤压而短掉一截。

我们可以在某一个方向上Dock多个控件,它们的行为就像被放在不同OrientationStackPanel中一样。

Grid

Grid,中文翻译为格,我们可以把它看成是一张看不见的表格。这样的排布方式,早在HTML静态网站盛行时就常被应用在网页的布局里。GridSilverlight提供了自由而强大的布局能力。

我们可以通过RowDefinitionsColumnDefinitions来定义行和列。例如,我们要创建一个32列的Grid

 

代码
    <Grid ShowGridLines="True">
        
<Grid.RowDefinitions>
            
<RowDefinition />
            
<RowDefinition />
            
<RowDefinition />
        
</Grid.RowDefinitions>
        
<Grid.ColumnDefinitions>
            
<ColumnDefinition />
            
<ColumnDefinition />
        
</Grid.ColumnDefinitions>
    
</Grid>    

 

 

为了看见定义的行和列,这里,我们把ShowGridLines设置为True。我们可以利用这个属性帮助我们进行布局,待布局完成了,再将其设置为False。以上XAML得到结果如下:

 

通过AttachedProperty,我们把控件放到指定的行和列中(默认为00列)。例如,以下代码会把一个按钮放到22列的Cell里。

 

    <Grid ShowGridLines="True">
...
        
<Button Content="Where am I?" Grid.Column="1" Grid.Row="1" />
    
</Grid>    

 

 

Grid里,行和列是从0开始数的,所以,22例应该设置其RowColumn1。运行效果如下:

 

对于行或者列的大小,我们可以通过设置,让其拥有3种不同的策略。

固定值

确定的像素数。这种策略可以应用于一些特别的应用,例如,定义一些空的行或列来代替Margin的值。其可扩展性在三种策略中最差。

自动大小

行或列自动选择大小,以供显示内容——内容需要多少空间,它就占用多少空间。

比例

每一行或列占用一定的比例。

例如,要形成一个高20像素的行,我们可以写:

 

<RowDefinition Height="20" />    

 

 

自动大小,可以使用“Auto”作为值:

 

<RowDefinition Height="Auto" />    

 

 

按比例:

 

            <RowDefinition Height="*" />
            
<RowDefinition Height="2*" />    

 

 

在星号前面添加权重值来改变所占的比例。当星号前面没有数字时,权重默认为1。因此,在上面的代码中,第一行占1/3,第二行占2/3

有时,我们需要添加一个跨行或者跨列的控件,这个的需求常常出现。我们可以通过设置Grid.SpanRow或者Grid.SpanColumn来实现。例如:

 

代码
    <Grid ShowGridLines="True">
        
<Grid.RowDefinitions>
            
<RowDefinition Height="20" />
            
<RowDefinition Height="*" />
            
<RowDefinition Height="2*" />
        
</Grid.RowDefinitions>
        
<Grid.ColumnDefinitions>
            
<ColumnDefinition />
            
<ColumnDefinition />
        
</Grid.ColumnDefinitions>
        
<Button Content="Where am I?" Grid.Column="1" Grid.Row="1" Grid.RowSpan="2" />
    
</Grid>    

 

 

由于在Button中设置了Grid.RowSpan2,这个Button会向下跨一行,得到如下效果,整个按钮占据了第二列的第二、三行:

 

有的时候,我们会希望用户根据页面内容或者自己的喜好,来调整Grid行或列的大小。Silverlight为我们提供了GridSpilitter的控件来完成这样的任务。例如,我们在上例中,添加一个纵向的GridSpiletter,用户可以自行调整左右部分的大小。

 

代码
    <Grid ShowGridLines="True">
        
<Grid.RowDefinitions>
            
<RowDefinition Height="20" />
            
<RowDefinition Height="*" />
            
<RowDefinition Height="2*" />
        
</Grid.RowDefinitions>
        
<Grid.ColumnDefinitions>
            
<ColumnDefinition />
            
<ColumnDefinition Width="Auto" />
            
<ColumnDefinition />
        
</Grid.ColumnDefinitions>
        
<Button Content="Where am I?" Grid.Column="2" Grid.Row="1" Grid.RowSpan="2" />
        
<controls:GridSplitter Grid.Column="1" HorizontalAlignment="Center" Margin="0,10,0,10" Width="10" Background="LightBlue" Name="GridSplitter1" VerticalAlignment="Stretch" Grid.RowSpan="3" />
    
</Grid>    

 

得到:

 

在一开始使用GridSpiltter的时候,你可能会觉得不大适应,有时候会得不到想要的效果,不能确定当用户拖动GridSpilitter的时候,不能确信界面上会发生什么。其实,要用好GridSpilitter,需要注意以下几点:

GridSpilitter放在单独的Cell里。虽然你也可以把GridSpilitter放在有内容的Cell里,但是,此时必须设置其它控件的Margin以免内容被GridSpilitter挡住,这等于是给自己添麻烦。不如放在一个单独的Cell里,然后设置这行或列的高度或宽度为Auto

GridSpilitter调整整个行或列的大小,而不会在意其自身占了几行或几列。因此,通常设置它的RowSpan或者ColumnSpan让它穿过所有的列或行。

要添加一个垂直的GridSpilitter,要设置它的VerticalAlignment为拉申;如果是水平GridSpilitter,则设置HorizontalAlignment为拉申。另一个值可以设置为固定大小。

如果行或者列设置了最小值,那么,GridSpilitter不会破坏最小值。

Canvas

事实上,大部分WinForm程序员最初转向Web开发时,总会怀念WinForm的布局方式,给一个起始点,然后设置一个大小,一切皆在掌控之间——多么简洁。呵呵,这个的模式虽然最简单。举个例子,要在两个紧靠在一起的按钮之间添加一个按钮,除了一添加按钮的代码之外,还需要调整原有的按钮的位置。如果按钮被放在StackPanel里,恐怕就不需要为按钮调整费劲了。

如果在设计时需要添加控件还好,要是在运行时要添加控件,那程序员会需要花很多时间在写控件而已的逻辑上。大家都做过一些项目,一定都会遇到类似的问题。

既然如此,为什么Silverlight里还要提供Canvas?因为如果你用它来编写一些基于坐标的程序时,它会带来很大的方便,例如把它作为一个画布来编写绘图工具,或者用它来开发一个需要坐标运算的游戏……

Canvas的使用也非常直接:

 

代码
    <Canvas>
        
<Button Canvas.Left="10" Canvas.Top="15" Content="Coordinate: 10,15" MinHeight="60" />
        
<Button Canvas.Left="60" Canvas.Top="80" Content="Corrdinate: 60,80" MinHeight="80" />
        
<Button Canvas.Left="100" Canvas.Top="90" Content="Corrdinate: 60,80" MinHeight="80" />
    
</Canvas>    

 

 

这里,定义了三个按钮,每一个按钮都设定了其在Canvas中的LeftTop。其中,两个按钮有一部分重叠,运行结果如下:

 

写在最后:

一个实际的页面,可能会同时运用到多种布局方式,实际项目中需要根据需求,灵活运用各种布局方式。

例如:

 

代码
    <Grid x:Name="RootLayout" ShowGridLines="True">
        
<Grid.ColumnDefinitions>
            
<ColumnDefinition Width="20" />
            
<ColumnDefinition Width="*" />
            
<ColumnDefinition Width="20" />
        
</Grid.ColumnDefinitions>
        
<Grid.RowDefinitions>
            
<RowDefinition Height="30" />
            
<RowDefinition Height="*" />
            
<RowDefinition Height="60" />
        
</Grid.RowDefinitions>
        
<StackPanel Grid.Column="1" Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Width="Auto">
            
<Button x:Name="btnOk" Content="OK" MinWidth="75" Height="24" Margin="5"/>
            
<Button x:Name="btnCancel" Content="Cancel" MinWidth="75" Height="24" Margin="5" />
        
</StackPanel>
    
</Grid>    

 

 

我们把一个StackPanel嵌在了一个Grid32列的位置,在StackPanel里,又横向的放了两人个Button

效果如下:

水平有限,如有谬误,还望各位高人多多指点。