Silverlight入门:第二部分 - 定义界面布局和导航
理解XAML应用程序中的布局管理是开发成功Silverlight应用的一个重要方面 。对于大多数来自Web领域的人来说,如果你对CSS不熟悉,那么这将成为你面临 的最大的挑战之一。
理解布局选项
Silverlight提供了一个灵活 的系统用于在页面上布置界面元素。布局模型同时支持绝对定位和相对定位的布 局风格。虽然提供了多种布局控件,但最常用的是:
Canvas
StackPanel
Grid
让我们逐个看看当把元素放在其中的时 候,它们是如何工作的。我们将使用一个简单的按钮元素来演示。我们使用本系 列第一部分创建的项目,并在Home.xaml页面作简单的演示。
Canvas
Canvas是最基础的布局控件,被用于通过坐标绝对地定位 元素。你可以通过在Canvas中附加属性来定位元素。附加属性允许父容器(在这 个例子中是Canvas)扩展在它之中的控件的属性(在这个例子中是按钮)。我们 可以在Canvas中像这样放置很多按钮:
1: <Canvas>
2: <Button Canvas.Top="50" Canvas.Left="50" Content="Button 1" FontSize="18" Width="150" Height="45" />
3: <Button Canvas.Top="150" Canvas.Left="20" Content="Button 2" FontSize="18" Width="150" Height="45" />
4: <Button Canvas.Top="70" Canvas.Left="80" Canvas.ZIndex="99" Content="Button 3" FontSize="18" Width="150" Height="45" />
5: </Canvas>
显示效果 如下:
可以看到,这是绝对定位布局的方法。注意到在这个例子中,我也可以通过 指定扩展属性ZIndex使得一个按钮叠加在另一个上面。这可能有助于像是游戏开 发或者高物理环境这样对计算非常精确的场合。Canvas在位置移动不太大,或是 你想漂亮地控制应用程序的尺寸时是很有用的。然而,Canvas有时不像 StackPanel和Grid那么容易利用。
StackPanel
StackPanel是垂直或水平堆叠元素的布局控件(默认是垂直的)。示例代码 如下:
1: <StackPanel>
2: <Button Margin="10" Content="Button 1" FontSize="18" Width="150" Height="45" />
3: <Button Margin="10" Content="Button 2" FontSize="18" Width="150" Height="45" />
4: <Button Margin="10" Content="Button 3" FontSize="18" Width="150" Height="45" />
5: </StackPanel>
显示效果如下:
1: <StackPanel Orientation="Horizontal">
2: <Button Margin="10" Content="Button 1" FontSize="18" Width="150" Height="45" />
3: <Button Margin="10" Content="Button 2" FontSize="18" Width="150" Height="45" />
4: <Button Margin="10" Content="Button 3" FontSize="18" Width="150" Height="45" />
5: </StackPanel>
显示效果如下:
StackPanel提供了一种使元素互相顶着或靠着的简单方式,让你不必花很大 精力在定位容器中的控件元素。
Grid
Grid是大多数情况下最灵活的布局(注意我说的是大多数,不是所有)。和 听起来一样,Grid通过行与列来安排位置。不像网站开发可能要在 <table>元素中放置<tr>、<td>标签那样,XAML Grid用法是 不同的。你定义Grid的整体结构以后,通过附加属性告诉元素把它们放在哪里。
考虑如下代码(注意我明确地显示了Grid的网格,但你不必每次都那么做, 我只是为了使显示更加形象化)。
1: <Grid ShowGridLines="True">
2: <Grid.RowDefinitions>
3: <RowDefinition Height="60" />
4: <RowDefinition Height="60" />
5: <RowDefinition Height="60" />
6: </Grid.RowDefinitions>
7:
8: <Grid.ColumnDefinitions>
9: <ColumnDefinition Width="175" />
10: <ColumnDefinition Width="175" />
11: <ColumnDefinition Width="175" />
12: </Grid.ColumnDefinitions>
13:
14: <Button Grid.Column="0" Grid.Row="0" Content="Button 1" FontSize="18" Width="150" Height="45" />
15: <Button Grid.Column="2" Grid.Row="0" Margin="10" Content="Button 2" FontSize="18" Width="150" Height="45" />
16: <Button Grid.Column="1" Grid.Row="2" Margin="10" Content="Button 3" FontSize="18" Width="150" Height="45" />
17: </Grid>
我们定义了一个3行3列的Grid并定义了宽和高。注意在按钮元素中我们使用 了附加属性来定位它们在Grid中的位置。
显示效果如下:
注意按钮上的附加属性(Grid.Column、Grid.Row)是如何告诉元素把自己定 位在容器的哪个位置的。在Expression Blend中做布局的工作是非常有帮助的。 注意在Blend中你可以通过向导可视地指定行和列的定义,同时它将为你生成 XAML:
箭头所指的向导告诉你行和列是否是固定尺寸的(锁定)。我们将在我们的 应用程序中使用多种布局控件。事实上,我们选择的默认模版已经为我们的应用 程序的基本框架提供了所有不同类型的布局控件。构建我们的Twitter应用程序
现在我们可以构建我们的应用程序了。总的来说,这是我们要做的程序的样 例。
注意到我们将为用户提供一个地方去输入搜索条件,并将结果通过列表显示 出来。我们也有导航区可以显示不同的内容,像是历史查询或是一些可能的统计 。
幸运的是我们选择的导航模版已经送了我们的整体布局一个大礼。我们只需 要在MainPage.xaml做一点点改动。在MainPage.xaml 的第29行,我们移除应用 程序的Logo,然后把它下面的内容改成Twitter Search Monitor。我们将很快回 来,不过现在先让我们切换到我们的视图。在视图文件夹的项目结构中右击并在 Visual Studio中选择创建新页,并选择创建一个新Silverlight页,将其命名为 Search.xaml:
现在你应该已经获得了一个包含默认Grid布局的空白XAML页面。我们 将在这里创建在上面看到的搜索屏幕。我们从MainPage.xaml获取头信息是因为 我们将使用框架元素来承载我们的应用程序。Silverlight导航框架
在这一节,让我们切入并了解Silverlight导航框架。如果你还记得我们 开始时使用的导航应用程序模版。默认的模版向我们提供了一个 MainPage.xaml 页面并给出了一个大致的主页视图。导航框架基本上由3部分组成:UriMapper、 Frame以及Page。
UriMapper
我喜欢把UriMapper元素当作是各种 各样的路由引擎。它并不总是必须的,但我认为它能模糊并简化你的导航终端。 你可以用/Home终端来映射 /View/Home.xaml终端,使得它更容易阅读并且不会 丢失任何技术配置。你以后还可以修改它,让它映射到其它地方。你可以看见 UriMapper作为MainPage.xaml页面中框架的一个元素:
1: <navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed">
2: <navigation:Frame.UriMapper>
3: <uriMapper:UriMapper>
4: <uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/>
5: <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>
6: </uriMapper:UriMapper>
7: </navigation:Frame.UriMapper>
8: </navigation:Frame>
现在像上面显示的那样,UriMapper在XAML里面。但它也可以包含资源,如果 要那样做的话,你可以像这样将它添加到框架元素中(假设资源是 UriMapperRoutes):
(此处原文未给出代码)
Frame
如果你是一名ASP.NET开发者,那么你可以把Frame元素当作是 ContentPlaceholder控件。Frame是一片可以被导航的区域。你可以指定一个默 认的视图,但是之后我们可以看到任何导航都可以在那片区域被触发。看上面的 代码,你可以看到一个默认的视图,Frame的 Source属性是为应用程序提供路由 的"/Home"。
Page
最后要说的基本导航区是只在最后一步创建的Page元素。它们是在Frame元素 中显示的非常基本的内容区域。它们与你通常添加的UserControl很相似,但是 特别之处在于它们可以导航。我们将考虑把我们的应用程序作为页面元素。
你可以通过以下视频了解更多关于导航的知识:
Silverlight的导航框架
简单地挖掘一遍它的功能就能受益无穷。框架允许深度连接到Silverlight应 用程序。
为我们的搜索视图创建界面布局
让我们为刚刚创建的Search.xaml页面创建一个界面。在这一节你可能对XAML 中所有的{StaticResource XXXXXXXX}元素感到困惑,我们将在第五部分的造型/ 模版章节作详细介绍,所以现在先跳过它们。注意我们的程序样例,我们需要一 个输入框、一个按钮和一个数据显示表格。让我们通过Blend来放置它们。要做 到这点,需要在Visual Studio中右击Search.xaml并选择在Blend中编辑:
在Blend中我们将放置一个两行的表格,一行用于放置搜索输入框和按钮,另 一行放置结果视图。在首行我们拖入一个StackPanel控件,并在其中放入一个 TextBox和一个Button,并将它的方向改为水平。
下一步我们将放置一个DataGrid用于显示数据。因为DataGrid不是核心控件 ,它在SDK库里,所以我们需要添加对它的引用。你可以在许多地方这样做,但 Blend其实会自动为你完成这样的工作。在Blend的工具箱中点击两次箭头并查找 DataGrid:
找到以后,选中它并把它拖到第二行。Blend会自动为你添加 System.Windows.Controls.Data.dll的引用并改变XAML文件中的标记。
1: <navigation:Page
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 : xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5: xmlns:mc="http://schemas.openxmlformats.org/markup- compatibility/2006"
6: mc:Ignorable="d"
7: xmlns:navigation="clr- namespace:System.Windows.Controls;assembly=System.Windows.Controls.Nav igation"
8: xmlns:data="clr- namespace:System.Windows.Controls;assembly=System.Windows.Controls.Dat a" x:Class="TwitterSearchMonitor.Views.Search"
9: d:DesignWidth="640" d:DesignHeight="480"
10: Title="Twitter Search Page">
11: <Grid x:Name="LayoutRoot">
12: <Grid.RowDefinitions>
13: <RowDefinition Height="32"/>
14: <RowDefinition/>
15: </Grid.RowDefinitions>
16: <StackPanel HorizontalAlignment="Left" Margin="0,-32,0,0" VerticalAlignment="Top" Grid.Row="1" Orientation="Horizontal">
17: <TextBox x:Name="SearchTerm" FontSize="14.667" Margin="0,0,10,0" Width="275" TextWrapping="Wrap"/>
18: <Button x:Name="SearchButton" Width="75" Content="SEARCH"/>
19: </StackPanel>
20: <data:DataGrid x:Name="SearchResults" Margin="0,8,0,0" Grid.Row="1"/>
21: </Grid>
22: </navigation:Page>
注意顶部的xmlns:data,这就是你为在XAML中使用非核心控件而添加的程序 集的引用。接着使用它们你就能在Grid中看到data:Grid元素。你的XAML现在看 上去应该和我一样,就像这样:
现在如果你回到Visual Studio你将会看到一个询问是否重新读取项目的提示 框。这是因为Blend添加了对DataGrid控件的引用,从而改变了项目文件。你可 以选择重新读取继续下去。这显示了开发工具是如何在项目文件层面进行整合的 ,现在我们可以再次用VS来编写代码了。
改变Search.xaml的默认UriMapper
现在我们只是创建了搜索页(这基本上是我们应用程序的主页),接着让我 们来为导航框架做一些改变。在MainPage.xaml找到Frame,并将默认的/Home改 为我们的搜索页,并且其它几处地方也做同样的更改。你的Frame XAML看起来应 该像这样:
1: <navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" Source="/Search" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed">
2: <navigation:Frame.UriMapper>
3: <uriMapper:UriMapper>
4: <uriMapper:UriMapping Uri="" MappedUri="/Views/Search.xaml"/>
5: <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/ {pageName}.xaml"/>
6: </uriMapper:UriMapper>
7: </navigation:Frame.UriMapper>
8: </navigation:Frame>
因为我们不需要Home.xaml,所以继续在项目中的其它地方删除它。同时添加 一个叫作History.xaml的新视图,同时改变MainPage.xaml的LinkBorder,使它 包含一个链接,像这样:
1: <Border x:Name="LinksBorder" Style="{StaticResource LinksBorderStyle}">
2: <StackPanel x:Name="LinksStackPanel" Style="{StaticResource LinksStackPanelStyle}">
3: <HyperlinkButton x:Name="Link1" Style="{StaticResource LinkStyle}" NavigateUri="/Search" TargetName="ContentFrame" Content="home"/>
4: <Rectangle x:Name="Divider1" Style="{StaticResource DividerStyle}"/>
5: <HyperlinkButton x:Name="Link2" Style="{StaticResource LinkStyle}" NavigateUri="/History" TargetName="ContentFrame" Content="history"/>
6: <Rectangle x:Name="Divider2" Style="{StaticResource DividerStyle}"/>
7: <HyperlinkButton x:Name="Link3" Style="{StaticResource LinkStyle}" NavigateUri="/About" TargetName="ContentFrame" Content="about"/>
8: </StackPanel>
9: </Border>
现在如果我们运行起来,效果应该像这样:
现在我们已经有了一个基础布局,让我们在第三部分添加数据。