Saturday, March 28, 2009

Silverlight 3 Beta Deep linking using Navigation application

The main disadvantage of earlier RIA technologies like Flash and Silverlight 2 was the absence of Deep linking.ie we can’t maintain a link to a specific screen or page in the RIA application.We can see just same URL even we change screens or pages in RIA application.Hence we can’t give a specific url to somebody or book mark that screen by url.Each time when the RIA application loads we have to navigate again to reach a specific page.That is very much time consuming.
Eg: If we want to have a profile page in our application how will we handle that in ASP.Net ? it’s simply addressed by using a page called profile.aspx with querystring ?id=10
This was not possible in Silverlight until the release of Silverlight 3 and it’s Navigation application support.With the introduction of navigation support we can maintain constant links to specific screens in the Silverlight RIA application.Hence we can distribute that link and bookmark that Silverlight page.

Creating a deep linked address book application using Silverlight
Since the main aim is to demonstrate deep linking ,I am using a simple Person class with properties Id ,Name and Address.The data storage is an Xml file.Here is the Person class.

public class Person
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
    }


Designing Data access
The PersonDataContext class provides the required Data.AllPersons and GetPersonById are the members which returns collection of all persons and Person by id respectively.


public class PersonDataContext
{
    XDocument _doc;
    private XDocument XmlDoc
    {
        get
        {
            if (_doc == null)
            {
                 _doc = XDocument.Load("Persons.xml");
            }
            return _doc;
        }
    }
    public IEnumerable<Person> AllPersons
    {
        get
        {
            return from li in XmlDoc.Root.Elements()
                   select new Person()
                   {
                       ID = Convert.ToInt32(li.Attribute("Id").Value),
                       Name = li.Attribute("Name").Value,
                       Address = li.Attribute("Address").Value
                   };
        }
    }
    public Person GetPersonById(int id)
    {
        XElement ele= XmlDoc.Root.Elements().First(s => s.Attribute("Id").Value == id.ToString());
        Person person=null;
        if(ele !=null)
        {
            person = new Person()
            {
                ID = Convert.ToInt32(ele.Attribute("Id").Value),
                Name = ele.Attribute("Name").Value,
                Address = ele.Attribute("Address").Value
            };
        }
        return person;
    }
}


Building User Interface

We are using a ListBox to display the list of persons in the HomePage.xaml.The ItemTemplate of the ListBox contains a hyperlink button and its NavigateUri is binded to Person object using a converter named Person2UriConverter.We are using a converter because the NavigateUri expects a object of type Uri.One more thing is that we don’t have path to details page in Person class.So we used a converter which combines the page url with the newly created name value pair.

HomePage.xaml


<navigation:Page 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:local="clr-namespace:AddressBook"
                 x:Class="SilverlightApplication1.HomePage"
                 xmlns:conv="clr-namespace:AddressBoook.Converters"
                 Title="HomePage Page">
    <navigation:Page.Resources>
        <conv:Person2UriConverter x:Key="p2uri" />
        <local:PersonDataContext x:Key="PersonDataContextDataSource" />
        <DataTemplate x:Key="personDT">
            <StackPanel>
                <HyperlinkButton Content="{Binding Name}" NavigateUri="{Binding Converter={StaticResource p2uri}}"></HyperlinkButton>
            </StackPanel>
        </DataTemplate>
    </navigation:Page.Resources>
    <Grid x:Name="LayoutRoot" Background="White" >
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <ListBox DataContext="{Binding Source={StaticResource PersonDataContextDataSource}}"
                 ItemsSource="{Binding Mode=OneWay, Path=AllPersons}"
                 ItemTemplate="{StaticResource personDT}" Grid.Row="1" />
        <TextBlock Text="Home" Style="{StaticResource HeaderTextStyle}"/>
    </Grid>
</navigation:Page>



Person2UriConverter.cs


public class Person2UriConverter:IValueConverter
    {
        #region IValueConverter Members
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            Person person = value as Person;
            string localpath = System.Windows.Browser.HtmlPage.Document.DocumentUri.LocalPath;
            return new Uri(localpath+"#/Views/Person.xaml?id="+person.ID,UriKind.RelativeOrAbsolute);
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
        #endregion
    }


Now we made the display of listbox with all persons.Now when run and click on the person link we can see the new url as http://localhost:61838/SilverlightApplication1TestPage.aspx#/Views/Person.xaml?id=1.It won’t display anything since there is no Person.xaml.Create the Person.xaml page which is going to be the detail view.

Creating detail page Person.xaml

Here the idea is to get the query string parameter id and load the Person class.This Person class is set as DataContext of the newly created a Page called Person.xaml where it is going to display the details.DataContext is setting on the overriden method OnNavigateTo.

Person.xaml.cs


// Executes when the user navigates to this page.
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            int personId = Convert.ToInt32(this.NavigationContext.QueryString["id"]);
            this.DataContext = new PersonDataContext().GetPersonById(personId);
        }


Person.xaml

<navigation:Page x:Class="SilverlightApplication1.Views.Person" 
           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="Person Page">
    <Grid x:Name="LayoutRoot"
          Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <TextBlock Text="ID" />
        <TextBox Grid.Column="1"
                 Text="{Binding ID}" />
        <TextBlock Grid.Row="1"
                   Text="Name" />
        <TextBox Grid.Row="1"
                 Grid.Column="1"
                 Text="{Binding Name}" />
        <TextBlock Grid.Row="2"
                   Text="Address" />
        <TextBox Grid.Row="2"
                 Grid.Column="2"
                 Text="{Binding Address}" />
    </Grid>
</navigation:Page>



Now when we click on the Person link we will get the detail page Person.XAML.Now you can distribute the URL to your friends and you can yourself use that as bookmark in your browser.

Sample can be downloaded from here.

No comments: