Page view counter

A Better Multi-Page Solution

Switch I've posted on multi-page Silverlight applications, and in fact have two videos (here and here) that show an approach that works quite well and that I was happy to steal at the time.

I recently received email from Lucas Stark (Senior Web Developer at Delta College) who suggested (quite correctly) that the pages should not have to find the PageSwitcher each time they want to navigate to another page, but rather they ought to be able to call a static method. 

I've tinkered with the code he provided, factoring out a few things and simplifying, and I now have a version that works as follows:

  • Any page wishing to participate in the switching may do so
  • If the page wishes to send data to the page it is calling, it must implement ISwitchable (defined below)

To make this work, I added ISwitchable.cs that defines the interface:

public interface ISwitchable
{
   void UtilizeState( object state );
}

I also added a new class, Switcher, in the file Switcher.cs

using System;
using System.Windows.Controls;

public static class Switcher
{
  public static PageSwitcher pageSwitcher;

  public static void Switch( UserControl newPage )
  {
    pageSwitcher.Navigate( newPage );
  }

  public static void Switch( UserControl newPage, 
                               object state )
  {
    pageSwitcher.Navigate( newPage, state );
  }

}
[code abridged leaving out test for pageSwitcher != null ]

This static class' PageSwitcher instance is set in the startup code in App.xaml.cs (where the root.visual is set as well)

 private void Application_Startup( object sender, StartupEventArgs e )
 {
    PageSwitcher pageSwitcher = new PageSwitcher();
    this.RootVisual = pageSwitcher;
    Switcher.pageSwitcher = pageSwitcher;
    Switcher.Switch( new Page() );
 }

Let's pause here and consider… when the application starts up, a new PageSwitcher (not seen yet) is created. This class, you may remember from previous coverage, is responsible for acting as the shell that holds the current page.  This instance of PageSwitcher is assigned

We'll look at it in just a moment. It is then assigned as the RootVisual (this value can only be set here and not changed while the program is running).  In addition, that same value is assigned to the static property pageSwitcher in the Switcher class.

This happens before anything else. Thus, upon start up that static class has a PageSwitcher instance available to it.

Finally, in the startup code, the static method Switch is called passing in a new instance of the Page user control.   We see above, that Switch is overloaded. The overload that takes a single argument takes a user control and calls Navigate on the newly instantiated PageSwitcher, passing along the user control. (There is a test to make sure that the PageSwitcher isn't null, but it really can't be, and so I've left that out here).

Let's look at PageSwitcher. The xaml has just an empty user control (as in previous versions), here is the .cs file:

public partial class PageSwitcher : UserControl
{
  public PageSwitcher()
  {
    InitializeComponent();
  }

  public void Navigate( UserControl nextPage )
  {
    this.Content = nextPage;
  }

  public void Navigate( UserControl nextPage, object state )
  {
    this.Content = nextPage;
    ISwitchable s = nextPage as ISwitchable;

     // test that s is not null or throw exception
     s.UtilizeState( state );
  }
}

 

When we call Navigate we pass in a new instance of Page so the first overload is called, and the content of the PageSwitcher class is filled with that user control, and the user sees the page as intended:

Page1
[this image has been cropped to save room]

When you click on switch, the event handler for that button is called and this is where the "great improvement" comes. In the previous version, each page had to look for the PageSwitcher (which was its parent), cast that value and then directly call the navigation. Now, all the page has to do is call a static method on the Switcher, passing in either a new page, or a new page and a value for the new page.

void SwitchToPage2_Click( object sender, RoutedEventArgs e )
{
  Switcher.Switch( new Page2(), YourName.Text );
}

As you can see, in this case, we call Page2 and pass in the text taken from the textBox.  Let's look at the code in Page2.xaml.cs,

using System.Windows;
using System.Windows.Controls;

namespace SimplePageSwitcherForBlog
{
  public partial class Page2 : UserControl, ISwitchable
  {
    public Page2()
    {
      InitializeComponent();
      Loaded += new RoutedEventHandler( Page2_Loaded );
    }

    void Page2_Loaded( object sender, RoutedEventArgs e )
    {
      SwitchToPage2.Click += new RoutedEventHandler( SwitchToPage2_Click );
    }

    public void UtilizeState( object state )
    {
      Message.Text = state as string;
    }

    void SwitchToPage2_Click( object sender, RoutedEventArgs e )
    {
      Switcher.Switch( new Page(), Age.Text );
    }
  }
}

The first thing to notice is that Page2 implements ISwitchable. And sure enough, we see that it does, by implementing the method UtilizeState. The implementation of that is to take the value we passed in, cast it to the string that it is, and assign that string to the Text property of the TextBlock named message in the first row (in red).

I've included the entire file because it is critical to note that this method is never called in this class, yet if you run the program you'll find that in fact the value is displayed! 

This "magic" is accomplished by delegating the responsibility to calling UtilizeState to the second overload of Navigate in PageSwitcher,

 public void Navigate( UserControl nextPage, object state )
 {
   this.Content = nextPage;
   ISwitchable s = nextPage as ISwitchable;
   if ( s != null )
   {
     s.UtilizeState( state );
   }
   else
   {
     throw new ArgumentException( "nextPage is not ISwitchable! "
       + nextPage.Name.ToString() );
   }
 }

When this method is called with two arguments, we know that the second argument is an object to be used in UtilizeState, so we call UtilizeState. To be careful, however, we first make sure that the userControl we've been given does in fact implement ISwitchable (as is required if you're going to use this method); if not we throw an exception.

This is very clean and works extremely well. The UserControl (page) only needs to know about calling the static Switch methods on a static class, the internals are entirely encapsulated and hidden from the consuming user controls, and on the flip side, the internal mechanism (the SearchPage and the Switcher, never need to know any of the semantics of any of the pages.

All of this is covered in detail, and placed in the context of a much more realistic scenario in my forthcoming tutorial (should be published in less than 2 weeks in both C# and VB). For now, the code used in this blog entry is available here.

Published Friday, November 21, 2008 1:35 PM by jesseliberty
Filed under:

Comments

# re: A Better Multi-Page Solution

See also blogs.msdn.com/.../silverlight-navigation-part-2.aspx

Hope that helps someone, - RC

Friday, November 21, 2008 3:22 PM by Roc1

# Silverlight Cream for November 21, 2008 - 2 -- #435

In this issue: Jeff Wolfer, Matt Watson, and Jesse Liberty. From SilverlightCream.com : Silverlight Attached

Friday, November 21, 2008 6:12 PM by Community Blogs

# re: A Better Multi-Page Solution

It is much cleaner as one of the first ideas.

I am looking forward to see the tutorial.

Peter Loebel

Switzerland

Friday, November 21, 2008 6:40 PM by SilverlightTravel

# re: A Better Multi-Page Solution

Sometimes it's best to let the expert do their best in the area they're good at and utilize their work. Telerik has developed a great set of components for Silverlight 2 and one area is the navigation with multi pages and be able to take advantage of their Deep Linking technology. Months ago, I started my own in-house multi page, but now I'm switching to a professional grade level with Telerik stuff. Take a look at it!

..Ben

Friday, November 21, 2008 6:46 PM by BenHayat

# re: A Better Multi-Page Solution

Dear Jesse, a static solution that has been posted a while ago (during Beta2) is available at flawlesscode.com - it's even better b/c it includes animated transitions  :)

Friday, November 21, 2008 7:00 PM by Maciek

# Eine bessere Multi-Page Solution at Programming with Silverlight, WPF & .NET

Pingback from  Eine bessere Multi-Page Solution at Programming with Silverlight, WPF & .NET

# re: A Better Multi-Page Solution

Ben,

With all respect, my friend, while I fully understand what you are saying, I think it depends on your goals. One can be expert and still choose a simpler "roll your own" solution because that is all you need, or because you choose not to depend on third party solutions or can't afford to pay for them, or because it teaches you more, or etc.

My examples are always intended to be the simplest solution that is correct and reasonable, but not necessarily the most robust or full featured. That is a pedagogical trade-off; on the one hand: the more features you put in the more complex the example becomes, and the less useful it is for teaching; on the other hand,  the simpler you make an example,  the more you run the riks of showing something that has little relationship to how you'd actually write it. A delicate balance.

In any case, and trying hard not to sound defensive, I don't mean to suggest that this is the final word on multi-page application design. :-)

Saturday, November 22, 2008 9:14 AM by jesseliberty

# re: A Better Multi-Page Solution

>>In any case, and trying hard not to sound defensive, I don't mean to suggest that this is the final word on multi-page application design. :-)<<

Not at all and I hope I didn't come across stepping on your blog post. I read all your posts but I know this multi pages can get deep and I was just suggesting the alternatives.

Peace ;-)

..Ben

Saturday, November 22, 2008 10:28 AM by BenHayat

# re: A Better Multi-Page Solution

Another alternative that uses a global Navigator is available at http://www.codeplex.com/NavFx

Saturday, November 22, 2008 11:35 AM by MikeHanson

# re: A Better Multi-Page Solution

Ben,

You are a buddy, never fear.  And I believe in open, honest and direct.

On a related note, I have learned that email and any proxy thereof is incredibly subject to misinterpretation -- nuances tend to be diminished, questions become accusations, suggestions become demands, and I've come to believe that email is for some reason read by the limbic system rathe than the cerebral cortex. Would make an interesting social psych experiment.

Oh, sorry about your account being discontinued and all your software licenses being revoked; total coincidence, I assure you.

Saturday, November 22, 2008 12:05 PM by jesseliberty

# Dew Drop &ndash; Weekend Edition &ndash; November 22-23, 2008 | Alvin Ashcraft's Morning Dew

Pingback from  Dew Drop &ndash; Weekend Edition &ndash; November 22-23, 2008 | Alvin Ashcraft's Morning Dew

# 2008 November 24 - Links for today &laquo; My (almost) Daily Links

Pingback from  2008 November 24 - Links for today &laquo; My (almost) Daily Links

# re: A Better Multi-Page Solution

Jesse First Tutorial (which I am using , hapily, limits to a buton/Click ..

how about a version using a composite button on a canvas that requires a mouseLeftButtonDown .. when I tried it with version one it compiled but didnt work ..

Wednesday, December 03, 2008 10:09 AM by Merrill Marie

# re: A Better Multi-Page Solution

Thankyou for the page switching solution you have provided. It is the best I have seen so far.  I had couple of questions, as Iam new to silverlight as well as .net.. Can I have the PageSwitcher as an actual page with a grid. And the navigate method fills in contents of a cell.  So imagine a page with header, footer and body, navigate fills in the body portion.

My next question is, will it be correct  to make the data that has to be transfered between the pages like 'Age.txt' - a static member of Switcher class. So that for example when a user logs in , I will fetch the user with WCF and hook it on the static User variable.  Other pages will use this information till logout.

My root question is wheather this static class resides on server or client.

Thanks again.

Anosh.

Tuesday, December 16, 2008 6:21 PM by anosh

# re: A Better Multi-Page Solution

thanks. you are a "project saver ".

Friday, July 31, 2009 10:03 AM by chocoboy