Silverlight 3 Beta 新特性解析(6) - Navigation和Deep Linking篇

前提条件:

阅读本文之前请确认你已经安装了如下软件

本篇主要内容:

Navigation

  • Frame和Page控件
  • 导航历史记录
  • NavigationService和NavigationContext

DeepLinking

  • UriMapper和UriMapping

导航(Navigation):

在Silverlight 2时代,如何从一个控件页面导航到另外一个控件页面是需要费很大功夫的事情

以至于国外有不少人研究并制作了自己的导航控件,如Peter BrownGerard Leblanc

但是可用性还是比较差,于是大家在进行Silverlight 2开发的时候碰到导航问题经常就卡壳了

Silverlight 3终于将导航框架引进了Silverlight

其主要依赖的两个控件是Frame和Page控件(和WPF一样)

  • Frame和Page控件

Frame控件用来放置Page控件并执行导航功能,其主要的属性和方法如下:

  • Source

用于设置第一次加载Frame时加载那个Page控件

如下就是加载Views目录下的EmployeePage.xaml页面控件

<navigation:Frame x:Name="NavFrame" Source="/Views/EmployeePage.xaml"/>
  • Navigate(Uri uri)

这个方法用于在不同页面间进行导航,其中Uri就类似于ASP.Net中的页面路径

只是这里的页面是.xaml而不是.aspx

如下表示名字为NavFrame的这个Frame控件现在将导航至Views目录下的ContactPage.xaml页面

this.NavFrame.Navigate(new Uri("/Views/ContactPage.xaml", UriKind.Relative));

而作为Frame控件的亲密战友,其和普通的用户控件事实上别无二致

除了其能被Frame调用,而且其有个Title属性用来设置这个页面标题并显示在浏览器上,如下

<navigation:Page x:Class="SL3Beta.Nav.Views.EmployeePage" 
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
           Title="Employee Page">

 

SL3Beta_nav01

    • 导航历史记录

    现在我们有了新的导航机制,就可以在浏览器上记录下曾经浏览过的页面了,如下图所示

    SL3Beta_nav02

    其主要是靠NavigationService中的GoBack以及GoForward来实现后退以及前进功能的

    • NavigationService和NavigationContext

    有了Frame来控制导航还是不够的

    比如下图是一个Page控件,我先点击查看联系信息按钮来查看雇员的联系信息

    SL3Beta_nav03

    而由于页面空间有限,所以我想把雇员信息放置到另外一个页面中

    而在这个Page控件如何导航到下一个页面呢

    答案是使用NavigationService类来导航(只有在Page控件中才起作用),如下

    private void ViewContactButton_Click(object sender, RoutedEventArgs e)
    {
        Employee employee = this.EmployeeGrid.SelectedItem as Employee;
        if (employee != null)
        {
            this.NavigationService.Navigate(new Uri(String.Format("/Views/ContactPage.xaml?ContactID={0}", employee.ContactID), UriKind.Relative));
        }
    }

    我们可以导航,而且还可以在页面间传参数

    大家是不是有点似曾相识的感觉啊,没错,这就是借鉴了ASP.Net采用QueryString来传递参数的机制

    现在留下来的问题就是如何在另外一个页面中获取得到传递过来的参数了

    这个工作是由NavigationContext来完成的,如下

    if (this.NavigationContext.QueryString.ContainsKey("ContactID"))
    {
        int contactID = Int32.Parse(this.NavigationContext.QueryString["ContactID"]);
    }

    下面我用一个实际的例子把这些知识串起来

    由于本篇将采用ADO.Net Entity Framework还有.Net RIA Services技术来获取并处理数据

    所以如果不了解的,请先查看下Silverlight 3 Beta 新特性解析(5) - Data篇

    也就是前面的如何创建项目以及采用ADO.Net Entity Data Model来获取数据

    并采用Domain Service类来提供网络服务的将一概略过

    本篇依然采用AdventureWorks数据库来示范

    本文将采用的是Employee和Contact表

    SL3Beta_nav04

    其通过ContactID将两个表关联起来

    本文的项目结构如下

    SL3Beta_nav05

    修改PersonDomainService.cs类如下

    public IQueryable<Contact> GetContactByContactID(int contactID)
    {
        return this.Context.Contact.Where(e=>e.ContactID==contactID);
    }

    也就是我们将通过输入contactID来获取雇员的联系信息

    而在SL3Beta.Nav项目下创建的Views目录用来存放页面控件

    MainPage.xaml是普通的用户控件,其代码如下

    <UserControl  x:Class="SL3Beta.Nav.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation">
        <Grid x:Name="LayoutRoot" Background="#606060">
            <navigation:Frame x:Name="NavFrame" Source="/Views/EmployeePage.xaml">
            </navigation:Frame>
        </Grid>
    </UserControl>

    在初始的状态下加载/Views/EmployeePage.xaml页面来显示雇员信息如下:

    <navigation:Page x:Class="SL3Beta.Nav.Views.EmployeePage" 
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
               xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
               xmlns:web="clr-namespace:SL3Beta.Nav.Web"
               xmlns:riaData="clr-namespace:System.Windows.Data;assembly=System.Windows.Ria.Controls"
               xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm"
               xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
               xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls"
               Title="Employee Page">
        <Grid x:Name="LayoutRoot">
            <StackPanel Width="900" Margin="10">
                <riaControls:DomainDataSource x:Name="EmployeeDataSource" LoadSize="10" LoadMethodName="LoadEmployee">
                    <riaControls:DomainDataSource.DomainContext>
                        <web:PersonDomainContext/>
                    </riaControls:DomainDataSource.DomainContext>
                    <riaControls:DomainDataSource.SortDescriptors>
                        <riaData:SortDescriptor Direction="Ascending" PropertyPath="EmployeeID"/>
                    </riaControls:DomainDataSource.SortDescriptors>
                </riaControls:DomainDataSource>
     
                <data:DataGrid x:Name="EmployeeGrid" ItemsSource="{Binding Data,ElementName=EmployeeDataSource}" MinHeight="100" SelectionChanged="EmployeeGrid_SelectionChanged"></data:DataGrid>
     
                <dataControls:DataPager PageSize="10" Source="{Binding Data,ElementName=EmployeeDataSource}"></dataControls:DataPager>
                
                <Button Content="查看联系信息" x:Name="ViewContactButton" HorizontalAlignment="Right" Margin="20,5" Click="ViewContactButton_Click" IsEnabled="False"/>
            </StackPanel>
        </Grid>
    </navigation:Page>

    SL3Beta_nav03

    其中查看联系信息按钮用来触发导航到选中的雇员的联系信息,其代码如下

    private void ViewContactButton_Click(object sender, RoutedEventArgs e)
    {
        Employee employee = this.EmployeeGrid.SelectedItem as Employee;
        if (employee != null)
        {
            this.NavigationService.Navigate(new Uri(String.Format("/Views/ContatctPage.xaml?ContactID={0}", employee.ContactID), UriKind.Relative));
        }
    }

    而ContactPage.xaml文件

    <navigation:Page xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm"  x:Class="SL3Beta.Nav.Views.ContactPage" 
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
               xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
               Title="Contact Page" Width="400" Height="400">
        <Grid x:Name="LayoutRoot">
            <dataControls:DataForm x:Name="ContactForm"></dataControls:DataForm>
        </Grid>
    </navigation:Page>

    将接收由HomePage.xaml传过来的参数如下,并通过服务来获取得到详细的联系信息并用DataForm显示出来

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        if (this.NavigationContext.QueryString.ContainsKey("ContactID"))
        {
            int contactID = Int32.Parse(this.NavigationContext.QueryString["ContactID"]);
     
            _personContext.Loaded += (sender, e2) =>
                {
                    if (_personContext.Contacts.Count>0)
                        this.ContactForm.CurrentItem = _personContext.Contacts[0];
                };
            _personContext.LoadContactByContactID(contactID);
        }
    }
    SL3Beta_nav06
    你也可以通过后退键来查看原来所有的雇员的详细信息

    Deep Linking:

    从上面的范例,我们可以看到,我们已经不知是能查看初始的载入的雇员页面

    我们也可以输入类似的网址如http://localhost:3066/Default.aspx#/ContactID=1006

    来直接查看联系编号为1006的雇员的联系方式,如下

    SL3Beta_nav07

    这就是传说中的深度链接了(Deep Linking)

    这样搜索引擎就可以搜索到下一级的页面了,改善了SEO效果

    但是这样可能会暴露网站的目录结构
    我们可以使用Uri映射来解决这个问题如下
    • UriMapper和UriMapping

    其中上述两个控件都位于System.Windows.Navigation这个名字空间中

    所以我们在MainPage.xaml文件中引用其如下

    xmlns:windowsNav="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"

    并修改MainPage.xaml的Frame控件如下

    <navigation:Frame x:Name="NavFrame" Source="Employee" HorizontalAlignment="Center" VerticalAlignment="Center">
        <navigation:Frame.Resources>
            <windowsNav:UriMapper x:Name="uriMapper">
                <windowsNav:UriMapping MappedUri="{}/Views/EmployeePage.xaml" Uri="Employee"/>
                <windowsNav:UriMapping MappedUri="{}/Views/ContactPage.xaml?ContactID={contactID}" Uri="ContactID={contactID}"/>
            </windowsNav:UriMapper>
        </navigation:Frame.Resources>
    </navigation:Frame>
    这样我们就将/Views/EmployeePage.xaml映射成Employee
    而/Views/ContactPage.xaml?ContactID={contactID}被映射成ContactID={contactID}
    对映射前和映射后的网络路径比较如下
    SL3Beta_nav10 SL3Beta_nav08
    SL3Beta_nav07 SL3Beta_nav09
    这样我们就隐藏了页面的位置,而且网络路径也更加美观,一举两得
     

    代码下载:


    作者:ibillguo
    出处:http://ibillguo.cnblogs.com/
    本文版权由作者和博客园共同所有,转载请注明出
    posted @ 2009-03-31 16:24  ibillguo  阅读(4495)  评论(16编辑  收藏  举报