在Silverlight里,导航框架允许开发者在Silverlight应用程序里实现一种方式来跳转到不同的页面,就像网站上的不同页面一样。这个框架也允许开发者创建历史使其与浏览器结合,使用户能使用浏览器的向前和向后导航。
Frame和Page对象
导航框架的两个主要对象是Frame和Page对象。Frame与ASP.NET母版页中的ContentPlaceHolder非常相似,是在一个页面放置不同views的地方。
练习:创建一个导航Silverlight应用程序
在这个练习里,会创建一个简单的应用程序包含两个超链接按钮和一个Frame。点击超链接会加载两个页面中的一个到Frame。Lets Go。
- 使用VS2010创建一个名为NavAppFromScratch的Silverlight应用程序。
- 打开MainPage.xaml,在Grid里,添加ShowGridLines="True"。定义Grid的单元格和两个超链接:
<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="30" /> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <HyperlinkButton Content="View 1" Click="LinkClick" Padding="5" /> <HyperlinkButton Content="View 2" Click="LinkClick" Padding="5" /> </StackPanel> </Grid>
- 下一步是添加导航框架到项目。首先添加System.Windows.Controls.Navigation.dll的引用
- 现在添加导航对象到项目中。需要在UserControl中手动添加System.Windows.Controls.Navigation到xml命名空间里。
<UserControl x:Class="NavAppFromScratch.MainPage" 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" xmlns:nav="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
- 添加一个Frame到最底的Grid的单元格里,命名为ContentFrame。代码如下:
<nav:Frame x:Name="ContentFrame" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="10" Grid.Row="1" BorderThickness="2" BorderBrush="Black"/>
- 在NavAppFromScratch项目里新建两个Silverlight页,名为Page1.xaml和Page2.xaml。分别代码如下:
<Grid x:Name="LayoutRoot"> <TextBlock Text="View 1" FontSize="60" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid>
<Grid x:Name="LayoutRoot"> <TextBlock Text="View 2" FontSize="60" Foreground="Blue" HorizontalAlignment="Center" VerticalAlignment="Center" /> </Grid>
- 现在要做的是点击按钮是加载页面到Frame。可以通过设置超链接按钮的Tag属性来定义要页面的源文件名。
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <HyperlinkButton Content="View 1" Click="LinkClick" Tag="/Page1.xaml" Padding="5" /> <HyperlinkButton Content="View 2" Click="LinkClick" Tag="/Page2.xaml" Padding="5" /> </StackPanel>
- 右击LinkClick选择“导航到事件处理程序”,使用Frame对象的Navigate方法来加载一个页面,添加代码如下:
private void LinkClick(object sender, RoutedEventArgs e) { HyperlinkButton button = (HyperlinkButton)sender; string viewSource = button.Tag.ToString(); ContentFrame.Navigate(new Uri(viewSource, UriKind.Relative)); }
- 运行,效果如下:
- 注意,你可以点击浏览器的导航按钮来向前和向后导航。
导航框架的优点
Silverlight4对比前一版本有了大幅改进,增加了一堆新特性,例如浏览器历史支持和深度链接(deep linking)。
深度链接(Deep Linking)
Silverlight4的另一个优点是支持深度链接。深度链接是链接到应用程序的一个特殊状态页面的能力。
为了解析深度链接,假设有个应用程序已经加载和显示了主页。当用户点击主页上的链接时,应用程序导航到产品列表页面。这时用户可以点击导航到一个产品的详细页面。这个应用程序可以用以下图表示:
如果你想有个链接直接导航到产品B的详细页面?使用导航框架,Silverlight允许开发者在应用程序里链接到不同的状态。
NavigationService对象
前面那一节里,使用了Frame对象的Navigate方法改变页面。这需要一次又一次地访问寄宿在页面内的Frame对象。例如,考虑下图,你可以从主页轻松导航到View1.但是你想在View1的后置代码里转到Inner View1的话,你需要访问寄宿在View1的Frame对象来导航到Inner View1。
好在,Silverlight的导航框架包含一个可以访问寄宿这View的Frame对象的对象。这个对象就是NavigationService。下面通过练习学习使用NavigationService对象。
练习:使用NavigationService对象
这个练习是在前面练习基础上的,需要在Page1里添加一个按钮和使用NavigationService对象导航到InnerView1的点击事件。
- 打开刚才项目的Page1.xaml,添加一个按钮。修改为代码如下:
<StackPanel> <TextBlock Text="View 1" FontSize="60" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Center" /> <Button Click="Button_Click" Padding="10" Content="Navigate to Inner View" HorizontalAlignment="Center" /> </StackPanel>
- 添加名为InnerPage的Silverlight页面,代码如下,和前面一样只是显示一个简单的文本。
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Background="Black"> <TextBlock Text="Inner View 1" FontSize="40" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/> </StackPanel>
- 在Page1.xaml后置代码里添加按钮的处理程序,来实现导航到InnerPage。
private void Button_Click(object sender, RoutedEventArgs e) { NavigationService.Navigate(new Uri("/InnerPage1.xaml", UriKind.Relative)); }
- F5运行可得到如下效果,点击Navigate to Inner View可跳转到InnerPage
这一节,学习了怎样使用NavigationService实现Silverlight页面的导航,下一节,将会学习使用NetworkContext对象在页面之间传递数据。
传递数据到导航页
在HTML页面,可以通过QueryString来传递数据到另一个页面。而在Silverlight应用程序是通过NavigationContext对象来传递数据的。例如,下面代码可以获得一个ProductID:
string productId = NavigationContext.QueryString["ProductID"].ToString();
练习:传递数据
在这个练习继续使用上节的项目,这里将会传递一些数据到InnerPage1.xaml里。
- 打开上一节的项目,打开Page1.xaml,修改代码来添加一个ComboBox,代码:
<StackPanel> <TextBlock Text="View 1" FontSize="60" Foreground="Green" HorizontalAlignment="Center" VerticalAlignment="Center" /> <Button Click="Button_Click" Padding="10" Content="Navigate to Inner View" HorizontalAlignment="Center" /> <ComboBox Padding="10" Margin="10" x:Name="Color" Width="100"> <ComboBoxItem Content="Blue" IsSelected="True" /> <ComboBoxItem Content="Red" /> <ComboBoxItem Content="Green" /> </ComboBox> </StackPanel>
- 接着打开page1.xaml的后置代码文件,编辑Button_Click事件处理程序,使用QueryString来传递选择的颜色:
private void Button_Click(object sender, RoutedEventArgs e) { string color = Color.SelectionBoxItem.ToString(); var uri = string.Format("/InnerPage1.xaml?Color={0}", color); NavigationService.Navigate(new Uri(uri, UriKind.Relative)); }
- 打开InnerPage1.xaml,再在下面添加一个TextBlock来显示传递过来的数据:
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" > <StackPanel Background="Black"> <TextBlock Text="Inner View 1" x:Name="ViewHeader" FontSize="40" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/> </StackPanel> <TextBlock Text="(Blue)" x:Name="ViewColor" FontSize="30" Foreground="Blue" HorizontalAlignment="Center" VerticalAlignment="Center" /> </StackPanel>
- 打开InnerPage1.xaml文件的后置代码,使用NavigationContext对象检索传递过来的数据,使用switch来处理颜色,代码如下:
protected override void OnNavigatedTo(NavigationEventArgs e) { var color = NavigationContext.QueryString["Color"]; Brush b; switch (color) { case "Red": b = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0)); ViewHeader.Foreground = b; ViewColor.Foreground = b; ViewColor.Text = "(Red)"; break; case "Green": b = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0)); ViewHeader.Foreground = b; ViewColor.Foreground = b; ViewColor.Text = "(Green)"; break; default: b = new SolidColorBrush(Color.FromArgb(255, 0, 0, 255)); ViewHeader.Foreground = b; ViewColor.Foreground = b; ViewColor.Text = "(Blue)"; break; } }
- F5运行,运行效果如下(InnerPage1页面浏览器的链接为:http://localhost:5379/NavAppFromScratchTestPage.aspx#/InnerPage1.xaml?Color=Red):
这一节学习了通过QueryString的方式使用NavigationContext对象来传递数据。下一节探讨Uri的映射和怎么创建友好的uri来导航页面。
Uri映射
上面的练习里,你可能注意到在Frame里导航到不同页面时,浏览器的URL会改变。可能也会注意到这些URL并不怎么好看,还包含了一些你可能不想显示的信息,例如下面的链接:
http://www.domain.com/Catalog.aspx#ProductDetails.xaml?ID=4
如果链接改为下面这样会好很多:
http://www.domain.com/Catalog.aspx#Product/4
这个URL易读很多,更加友好。而且,它不会透露你应用程序的细节,可以通过使用Uri映射的特性来获得这个链接。
练习:使用Uri映射
这个练习里,将上面的项目改为使用Uri映射的方式来导航。
- 打开上面的项目。这里有三个页面需要使用Uri映射,Page1.xaml,Page2.xaml,InnerPage1.xaml。
- 打开App.xaml为导航框架添加xml命名空间:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:nav="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation" x:Class="NavAppFromScratch.App" >
- 添加UriMapper到Application Resources里,在UriMapper里,现在需要添加三个UriMapping元素,如下:
<Application.Resources> <nav:UriMapper x:Key="uriMapper"> <nav:UriMapping Uri="Page1" MappedUri="/Page1.xaml"/> <nav:UriMapping Uri="Page2" MappedUri="/Page2.xaml"/> <nav:UriMapping Uri="InnerPage/{c}" MappedUri="/InnerPage1.xaml?Color={c}" /> </nav:UriMapper> </Application.Resources>
- 现在主页面上的两个超链接导航按钮可以改为如下:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <HyperlinkButton Content="View 1" Click="LinkClick" Tag="Page1" Padding="5" /> <HyperlinkButton Content="View 2" Click="LinkClick" Tag="Page2" Padding="5" /> </StackPanel>
- 同样的,在后端也要修改,打开Page1.xaml.cs,修改按钮处理事件如下:
private void Button_Click(object sender, RoutedEventArgs e) { string color = Color.SelectionBoxItem.ToString(); var uri = string.Format("InnerPage/{0}", color); NavigationService.Navigate(new Uri(uri, UriKind.Relative)); }
- 最后在MainPage.xaml里把UriMapper属性添加到Frame里:
<nav:Frame x:Name="ContentFrame" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Margin="10" Grid.Row="1" BorderThickness="2" BorderBrush="Black"
UriMapper="{StaticResource uriMapper}"/> - 运行效果:
http://localhost:5379/NavAppFromScratchTestPage.aspx#InnerPage/Red
Uri路由
除了Uri映射外,Silverlight的导航框架还支持Uri路由。例如,如果你把所有页面放在名为“Views”的文件夹里,你可以按照这个命名约定设置理由映射:
<nav:UriMapping Uri="{}{p}" MappedUri="/Views/{p}.xaml" />
这个映射可以匹配所有在“Views”里的xaml文件。例如,View1可以匹配/Views/Views1.xaml。