| Comments

This is part 5 in a series on getting started with Silverlight.  To view the index to the series click hereYou can download the completed project files for this sample application in C# or Visual Basic.

In our previous step we added better data binding and saved some data to our isolated storage area.  Let’s start integrating some other controls to make our experience a little better.

AutoCompleteBox

Remember the history data we save every time a search term is used?  Let’s help our users search better, by providing them a history of their searches in the TextBox while they type.  We’re going to use a control from the Silverlight Toolkit to accomplish this, AutoCompleteBox.

To do this we need to add a reference to the System.Windows.Controls.Input assembly.  Then add an xmlns to your Search.xaml file:

   1: xmlns:input="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input"

With this in place, change the control named SearchTerm from TextBox to input:AutoCompleteBox:

   1: <input:AutoCompleteBox x:Name="SearchTerm" FontSize="14.667" Margin="0,0,10,0" Width="275"
   2:      IsTextCompletionEnabled="True" />

Now we need to provide data for the AutoCompleteBox.  In our Helper.cs class I added the following function:

   1: internal static string[] GetSearchTermHistory()
   2: {
   3:     List<string> searchHistory = new List<string>();
   4:  
   5:     foreach (var item in IsolatedStorageSettings.ApplicationSettings.Keys)
   6:     {
   7:         searchHistory.Add(item.ToString());
   8:     }
   9:  
  10:     return searchHistory.ToArray();
  11: }

Then in the Loaded event handler for Search.xaml.cs I added a call to Helper.GetSearchTermHistory():

   1: void Search_Loaded(object sender, RoutedEventArgs e)
   2: {
   3:     SearchResults.ItemsSource = pcv; // bind the DataGrid
   4:     _timer.Start(); // start the timer
   5:     SearchForTweetsEx(); // do the initial search
   6:     SearchTerm.ItemsSource = Helper.GetSearchTermHistory(); // populate autocomplete
   7: }

The result of which now is that when the application loads it will give the user some hinting while they search:

AutoCompleteBox in action

Helpful!

Adding our History view

Now that we have our search history, we can provide some data in a new view called History.xaml.  You should have already created this in the Views folder in a previous step, but if you haven’t, do so now (using the Silverlight Page item template).  Here we’ll want to show perhaps a simple list of all the terms.  We can easily do this using a ListBox in our XAML like this:

   1: <StackPanel>
   2:     <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}" 
   3:                    Text="Serach Term History"/>
   4:     <ListBox x:Name="SearchTermHistory" />
   5: </StackPanel>

Then using a function we already have in Helper.cs, we can bind new data to the listbox like this in History.xaml.cs:

   1: protected override void OnNavigatedTo(NavigationEventArgs e)
   2: {
   3:     SearchTermHistory.ItemsSource = Helper.GetSearchTermHistory();
   4: }

So we are able to make re-use of a function to display a complete history of our terms:

History pane view

Adding some more navigation functionality

Now that we have a couple of views in the application, notice the navigation framework working.  You can navigate using the buttons, but also the back/forward browser buttons will also trigger the same functionality!

We can actually take this to the next level with the History view now.  In the ListBox I’m adding a SelectionChanged event handler:

   1: <ListBox x:Name="SearchTermHistory" SelectionChanged="SearchTermHistory_SelectionChanged" />

The function looks like this in History.xaml.cs:

   1: private void SearchTermHistory_SelectionChanged(object sender, SelectionChangedEventArgs e)
   2: {
   3:     this.NavigationService.Navigate(new Uri(string.Format("/Search/{0}", 
   4:             SearchTermHistory.SelectedItem.ToString()), UriKind.Relative));
   5: }

Notice the URI format I’m using?  It will end up being /Search/{term}.  We need to instruct our Navigation Frame in the application to map that accordingly.  Go back to MainPage.xaml and find the UriMapper content and add this line:

   1: <uriMapper:UriMapping Uri="/Search/{term}" MappedUri="/Views/Search.xaml?term={term}" />

Now we need to make our Search.xaml page understand this.  In Search.xaml.cs in the OnNavigatedTo I add this functionality:

   1: protected override void OnNavigatedTo(NavigationEventArgs e)
   2: {
   3:     if (this.NavigationContext.QueryString.ContainsKey("term"))
   4:     {
   5:         SearchTerm.Text = this.NavigationContext.QueryString["term"].ToString();
   6:         SearchForTweetsEx();
   7:     }
   8: }

Now when a user goes to the History page and selects an item, it automatically executes the search!

Summary

Integrating other controls and 3rd party controls like this will help define your user experience and hopefully provide an better experience for your users of the application.  There are a bunch of 3rd party control vendors producing great sets of components.  Visit my Silverlight Controls post for a list of them.  Be sure to check out the Silverlight Toolkit for others from Microsoft and a sample application showing all the controls.

We’ve got our application pretty well working, but the UI could use some more polish, in the next step in part 6 let’s show how we can better template this without affecting functionality.

| Comments

With the Silverlight 3 Tools for Visual Studio, developers get access to the Silverlight Navigation Application template.  By default the styles (which are located in the project’s Assets/Styles.xaml file) are really good and neutral for the application layout.  Now, keep in mind this is an overall application layout style and not control styles/templates.

In addition to the default look provided by the tools out of the box with Silverlight 3, we’re providing 7 additional application template themes for you to enjoy, extend, whatever.  Corrina and team had developed these for the community to consume and we’ve put them up on the Expression Community Gallery for download.  Here’s a preview with the links to each template:

Aurora (also available for RIA Services Template):

Aurora app theme

Frosted Cinnamon Toast (also available for RIA Services Template):

Frosted Cinnamon Toast app theme

Lime Shocker (also available for RIA Services Template):

Lime Shocker app theme

Pinky (also available for RIA Services Template):

Pinky app theme

Retro:

Retro app theme

Skyline:

Skyline app theme

Subdued:

Subdued app theme

There you go, we hope you enjoy them.  Download the zip file and you will see a Styles.xaml file included.  Simply put this in the applications Assets folder, replacing the default and you will automatically have the new theme in your application.

Hope this helps!

| Comments

I’ve been playing around with the Silverlight 3 navigation framework some more (thanks for the comments/thoughts on the last post about sharing data).  I got a few emails about understanding how the navigation works and people coming up with interesting uses.  Let’s take a moment to explore two of these concepts: out-of-browser navigation and controlling your navigation in your app.

Navigation Basics

If you are using Visual Studio 2008 and the Silverlight 3 tools, you’ll notice that when you choose to create a new project (or perhaps you didn’t notice and this will be new to you) that you have an option to create a Silverlight Navigation Application which is a starter template for some basic navigation framework.  This isn’t required to use the navigation framework, but merely a starting point.

Silverlight Navigation Application template

Once you have this template and click on the different navigation buttons (and/or add your own as well) you’ll see that the content changes based on the event.  Additionally you should notice that the browser’s back/forward buttons integrate with this functionality and allow you to navigate back/forward using the browser controls as well, honoring the navigation context of the application.

Out-of-browser Navigation

So now you’ve finished your app, complete with navigation and also added the functionality to allow the application to be installed out-of-browser.  Yipee!  Your users install the application offline and boom, they call you and say “how do I go back to where I was?”  Well, ideally you’ve created an application that has good navigation points in it for them, but perhaps you want to add simple forward/back functionality while in out-of-browser mode similar to the in-browser buttons that come with their preferred Internet browsing software.

Guess what?  The navigation framework provides some APIs to help you that are a part of the Frame that you are navigating:

  • CanGoForward/CanGoBack
  • GoForward/GoBack

With these two you can emulate the same browser back/forward functionality.  Here’s an example using the default VS2008 navigation template.  In MainPage.xaml I’ve removed the branding information (your.application.name) and added the following (about line 30):

   1: <StackPanel Style="{StaticResource BrandingPanelStyle}" x:Name="JournalNav">
   2:     <Button Content="back" Style="{StaticResource PageLinkStyle}"
   3:         x:Name="NavBack" Click="NavBack_Click" />
   4:     <Button Content="forward" Style="{StaticResource PageLinkStyle}" 
   5:         x:Name="NavFwd" Click="NavFwd_Click" />
   6: </StackPanel>

The NavBack/NavFwd click functions look like this:

   1: private void NavBack_Click(object sender, RoutedEventArgs e)
   2: {
   3:     this.Frame.GoBack();
   4: }
   5:  
   6: private void NavFwd_Click(object sender, RoutedEventArgs e)
   7: {
   8:     this.Frame.GoForward();
   9: }

Now on the Frame that I’m using I added an event handler for Navigated that looks like this:

   1: private void Frame_Navigated(object sender, NavigationEventArgs e)
   2: {
   3:     NavBack.Opacity = Frame.CanGoBack ? 1 : 0;
   4:     NavFwd.Opacity = Frame.CanGoForward ? 1:0;
   5: }

And finally I’ve added a Loaded event handler to MainPage to handle the initial load:

   1: void MainPage_Loaded(object sender, RoutedEventArgs e)
   2: {
   3:     JournalNav.Visibility = App.Current.RunningOffline ? Visibility.Visible : Visibility.Collapsed;
   4: }

The end result is the application in-browser works fine and uses the browser software back/forward buttons.  When installed out-of-browser, you’ll notice that an application-specific “back” and “forward” links are created (only when they are applicable) and the user can still navigate the frame that way.

In browser:

Navigation in-browser

Out-of-browser:

Navigation out-of-browser

Simple enough and I would not have had to change anything about how I already used the navigation framework in my application.

Controlling your navigation history

The other comment I’ve got is if people wanted to have ultimate control over their history navigation and have some parts integrate with the browser and other parts not.  Let me introduce you to a property of Frame: JournalOwnership.  By default the setting is Automatic, which means that the frame integrates with the browser history journal.  Setting it to something else, like OwnsJournal for example, means that it will not automatically integrate and you are now responsible for that navigation. 

What this means is that you can have a frame that is independent of the browser navigation, but still leverage the power of the framework.  Let’s alter our default template and add one of our navigation pages to include it’s own navigation pages, but those we don’t want to be a part of the overall app. 

I’ve modified the MainPage to add a link to another page called SubNavigation.xaml which then looks like this:

   1: <StackPanel>
   2:     <StackPanel x:Name="JournalNav" Orientation="Horizontal">
   3:  
   4:         <Button Click="NavButton_Click" Tag="/Views/SubNav/SubNav1.xaml" Content="section 1" 
   5:                     Style="{StaticResource PageLinkStyle}"/>
   6:         <Button Click="NavButton_Click" Tag="/Views/SubNav/SubNav2.xaml" Content="section 2" 
   7:                     Style="{StaticResource PageLinkStyle}"/>
   8:  
   9:         <Button Content="back" Style="{StaticResource PageLinkStyle}" 
  10:             x:Name="NavBack" Click="NavBack_Click" />
  11:         <Button Content="forward" Style="{StaticResource PageLinkStyle}" 
  12:             x:Name="NavFwd" Click="NavFwd_Click" />
  13:     </StackPanel>
  14:     <navigation:Frame x:Name="SubFrame" JournalOwnership="OwnsJournal"
  15:             HorizontalContentAlignment="Stretch" Source="/Views/SubNav/SubNav1.xaml"
  16:                           VerticalContentAlignment="Stretch"
  17:                           Padding="15,10,15,10"
  18:                           Background="White"/>
  19: </StackPanel>

You’ll see it looks similar (just for demonstration purposes) and includes a Frame.  Notice the JournalOwnership property set to OwnsJournal.  Now I also have the same back/fwd links  and functions to control them (GoBack/GoForward) in this sample, but now since I (well, SubFrame) owns the journal, I’m responsible for the navigation.  So here’s what happens:

  • User navigates to app
  • User clicks “about” – browser history appended
  • User clicks “sub navigation” – browser history appended
  • Within the “sub navigation” page, user clicks “subnav 2” – browser history not updated
  • User clicks “back” within the “subnav 2” page – user is taken back to “subnav 1”

So By changing the JournalOwnership, I get the control to manage the journal navigation, but also have to remember that.

Summary

I hope these two little experiments help you look at the navigation feature in some new ways.  It’s really a great feature I’m having fun thinking about things out loud with you all and getting feedback and more ideas.  I can’t wait to see great Silverlight 3 applications built with our framework!

The code that I used for these experiments is available here: slnavexperiments1.zip.  You will need the Silverlight 3 developer tools to run this code.  You can obtain all those tools here at the Silverlight 3 beta information section.

Hope this helps!

| Comments

I got an email the other day about if there was a way to pass an object between the navigation pages in Silverlight 3.  The scenario was that the developer wanted to use the same data, but represent it visually in different ways.

Silverlight 3 introduces a new navigation framework in the runtime making it easier to navigate to different areas of an application and assist in ‘deep linking’ concepts for applications.  More resources:

At first my reaction was “no, we don’t allow that easily” but then I thought about it a bit and played around with a sample in the context of this developer’s use case.  Right now, the way you can quickly pass chunks of data to another page is via query string mechanisms.  So in my code I can say:

   1: myFrame.Navigate(new Uri("/foo.xaml?customerId=1234", UriKind.Relative));

And then in the navigated page something like:

   1: string customerId = this.NavigationContext.QueryString["customerid"];

This works fine for string/simple data.  In fact I could probably serialize a simple type and send it this way as well…but I don’t think that was the desired intent of this developer.  But if you think about the concept of the Frame, then you can start thinking about DataBinding techniques and how we can use the Frame as our container.  Allow me to think out loud…

Let’s use the default Silverlight Navigation Application template in the Silverlight 3 Tools for Visual Studio.  This will give us enough stub to work with.  If you look at MainPage.xaml you’ll see that the page has one Frame element in it:

   1: <navigation:Frame x:Name="Frame" Source="/Views/HomePage.xaml"
   2:       HorizontalContentAlignment="Stretch"
   3:       VerticalContentAlignment="Stretch"
   4:       Padding="15,10,15,10"
   5:       Background="White"/>

This is the core element that is going to receive navigation commands and change its content based on those commands.  Now in the Loaded event handler of MainPage, let’s grab some data.  I’m using a simple class here that just iterates a Person type (mainly so I can include it in the sample easily).  My Loaded event handler now looks like this:

   1: void MainPage_Loaded(object sender, RoutedEventArgs e)
   2: {
   3:     People p = new People();
   4:     List<Person> peeps = p.GetPeople();
   5:  
   6:     this.Frame.DataContext = peeps;
   7: }

So you can see that I’m now setting the DataContext of the Frame element to my List<Person>.  So now let’s crack open some of the other pages.  Again, remember the scenario: same data, different views.  Open the Views/HomePage.xaml and let’s show a view of just a simple list of names.  I added a ListBox and just displayed the FullName property of my data:

   1: <StackPanel> 
   2:     <TextBlock Text="Name List" Style="{StaticResource HeaderTextStyle}"/>
   3:     <StackPanel Style="{StaticResource ContentTextPanelStyle}">
   4:         <ListBox x:Name="PeopleList" DisplayMemberPath="FullName" 
   5:             ItemsSource="{Binding}" />
   6:     </StackPanel>
   7: </StackPanel>

Notice that there is going to be no code here in the source file for HomePage.xaml.cs – we are using the {Binding} property for the ItemsSource…essentially trickling down the DataContext from the Frame.  Remember, that HomePage.xaml is essentially a child control of the Frame so it is aware of the DataContext.  Now let’s go into AboutPage.xaml for our more detailed view, showing a DataGrid of all the elements of the data:

   1: <StackPanel>
   2:     <TextBlock Text="Detail" Style="{StaticResource HeaderTextStyle}"/>
   3:     <TextBlock Text="Detail list of members with gender." 
   4:             Style="{StaticResource ContentTextStyle}"/>
   5:     <data:DataGrid ItemsSource="{Binding}"/>
   6: </StackPanel>

Again, no code here to retrieve the data or wire it up – using {Binding} to the DataContext again.  So now with one data fetch and binding to our navigation context (Frame) we can re-use that same object across different views and the result shows us different levels of detail.

View 1: simple listView 2: detail list

So at least that is one thought of how to share the information.  What do you think?  Obviously it will not work in all scenarios, but also remember you can have more than one navigation frame in your application too!  Here’s the sample code for these thoughts: SilverlightApplication32.zip

Hope this helps!

| Comments

One of the new features in Silverlight 3 is providing an application navigation framework via the Frame and Page controls in the APIs.  If you saw my guide to Silverlight 3, you may have seen the section on navigation which describes the functionality and as well has a link to a video tutorial about it.

I wanted to augment that tutorial with some additional information about URI routing, which I think is one of the great features of the framework.  You see typically your UriMapper might have a 1:1 mapping and look something like this (note in my sample here I’m putting this in App.xaml, but the model may change in the future of where you can/should place UriMapper):

   1: <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   2:              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   3:              x:Class="NavigationSample.App"
   4:              xmlns:navcore="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"
   5:              >
   6:     <Application.Resources>
   7:         <navcore:UriMapper x:Key="uriMapper">
   8:             <navcore:UriMapping Uri="About-Us" MappedUri="/Views/AboutPage.xaml" />
   9:         </navcore:UriMapper>
  10:     </Application.Resources>
  11: </Application>

But what if you still wanted to provide friendlier routes, obfuscate the physical location of the view, but handle the default scenario?  You can as long as you follow your own naming conventions.  Take for instance if we contain all our pages in a folder called Views in our application.  We could then map our routes like this:

   1: ...
   2: <navcore:UriMapper x:Key="uriMapper">
   3:     <navcore:UriMapping Uri="{}{page}" MappedUri="/Views/{page}.xaml" />
   4: </navcore:UriMapper>
   5: ...

You could also follow some naming conventions if you wanted to make sure you limited it to only pages or something so you require the view page to be named SomethingPage.xaml and then can have a route like:

   1: ...
   2: <navcore:UriMapper x:Key="uriMapper">
   3:     <navcore:UriMapping Uri="{}{page}" MappedUri="/Views/{page}Page.xaml" />
   4: </navcore:UriMapper>
   5: ...

The navigation routes are read top down so you can still have explicit (or additional) routes in addition to the default.  So given:

   1: ...
   2: <navcore:UriMapper x:Key="uriMapper">
   3:     <navcore:UriMapping Uri="About-Us" MappedUri="/Views/AboutPage.xaml" />
   4:     <navcore:UriMapping Uri="History" MappedUri="/Views/AboutPage.xaml" />
   5:     <navcore:UriMapping Uri="{}{page}" MappedUri="/Views/{page}.xaml" />
   6: </navcore:UriMapper>
   7: ...

A request to About-Us, History or About would navigate to /Views/AboutPage.xaml regardless.  This provides you some flexibility and granularity in your implementation and also provides additional SEO points for your content.

I really like the UriMapper in the navigation framework and have been lobbying to change the default templates Visual Studio spits out to use the UriMapper by default.  It provides you with a cleaner (and perhaps shorter) URI as well as obfuscates your “code” from your URI logic.  Be sure to check out the video if you aren’t familiar with the framework at all.

I hope this helps and would love to know what you think about the navigation framework?


This work is licensed under a Creative Commons Attribution By license.