Jesse Liberty - Silverlight Geek

By, For and About Silverlight Developers

Multi-Page Applications in Silverlight

I wanted to build a multi-page (Search - Results) application when I realized that isn't really our model. Fortunately, I wrote to Ashish Shetty , who combines enormous knowledge with enormous kindness, and even though he is in the middle of blogging about this, he agreed that I could blog about it as well. Since I will be creating an HDI Video on this as one of my first Beta 2 videos (RSN) and since I think his solution is wicked cool, here is how you do it

1. Create a new Silverlight 2 project in Visual Studio , (I named mine, MultiPage).

2. You want to have at least three pages. Let's use the default Page.xaml as the first. Add any content you like but  but include a button with a name and set the content to something like "Switch"

3. Right click on the project and click Add New Item, and add a second User Control. Name the new user control anything you want and give it any content you want. I'll call mine Page2.xaml and give it content to make it distinct: 

<UserControl x:Class="MultiPage.Page2"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="Beige">
        <StackPanel Orientation="Horizontal">
        <TextBlock Text="This is Page2" FontFamily="Comic Sans MS" 
            FontSize="48" VerticalAlignment="Center" />
        <Button x:Name="SwitcherooButton" Content="Switcheroo" 
            Height="40" Width="80" 
            Background="Red" FontSize="14" 
            Margin="20,0,0,0" />
        </StackPanel>
    </Grid>
</UserControl>

 

Page2Switcher

Note that again I have a button for switching pages.

 

Here is where the magic happens

4. Add a third page (I named mine PageSwitch.xaml.)  Put nothing in the xaml file, because the content will be the page you want to display,  but in the code-behind file add this method:

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

5. Wire up the buttons on all of your pages to invoke the Navigate method, passing in the page you want to navigate to.

So, in Page.xaml.cs you'll add this code,

public Page()
{
    InitializeComponent();
    SwitchButton.Click +=
          new RoutedEventHandler(SwitchButton_Click);
}
 

void SwitchButton_Click(object sender, RoutedEventArgs e)
{
    PageSwitcher ps = this.Parent as PageSwitcher;
    ps.Navigate(new Page2());
}

 

6. Finally, in App.xaml.cs change the startup page from Page.xaml to PageSwitcher.xaml,

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

 

What's Going On Here??

The way this all works is that when your program starts, the opening page is now PageSwitcher whose content is blank, but whose constructor sets it to whatever you want your first page to be (in this example,  Page.xaml.)  Since all pages are actually user controls, they work fine as the content of a page.

When you click the switch button, you find the parent of your page which is the PageSwitcher page and that has a Navigate method that you invoke, passing in the page you want to navigate to. In this case, we're hardwiring the change, but it wouldn't be hard to make that value be more dynamic.

Navigate causes the PageSwitcher page to change its contents from whichever page it is currently displaying to whichever page you ask it to display, and the garbage collector cleans up the old.

(If you need to keep old values around between switches, create both pages and don't call new, but that will use more memory, or stash the values you need inside member variables of PageSwitcher -- more on that when I do a video on this approach).

 

It is a very cool, very fast effect switching from one page to the other.

SwitchToPage1 SwitchToPage2

 

Here's the complete source code

Comments

BenHayat said:

Great blog Jesse.

One minor typo. You wrote:

>>4. Add a third page (I named mine PageSwitch.xaml.)<<

That should be "PagerSwitcher.xaml" to match the codes you wrote in the bottom.

I had been playing with this concept and now I have two styles/models of Page programming. One that works like WinForm "Modal" type, where you go down into inner pages and then you back out and all the child pages can see their parents data. And as you backout, you remove the page from the parent collection, where the memory gets released.

The second one (as you demonstrated) works in sequential model like Web Pages where there is only one page active at a time. But the developer must create some sort of "Master menu" that would allow the user to jump anywhere.

Question: When this command is executed "ps.Navigate(new Page2())", what's going to happen to the previous pageSleep that had been created in the memory. I'm concern about that.

Thanks!

# May 31, 2008 1:47 PM

jesseliberty said:

>>what's going to happen to the previous page that had been created in the memory<<

There no longer being a reference to it, it will be cleaned up by the garbage collector. The idea of this approach is that it uses less memory; as you replace the content, you replace the memory usage.

# May 31, 2008 2:02 PM

Wounded_Ego said:

Thanks, this looks like it will be useful.

However, I'm not the brightest bulb in the marquee, I'm afraid, so maybe you can help me with something...

You supplied all the source code, but no solution or project file. How do I handle that in VS2008? I can create a blank solution but how do I create a blank project in which to host this?

Thanks,

# May 31, 2008 7:30 PM

jesseliberty said:

>>You supplied all the source code, but no solution or project file. How do I handle that in VS2008? I can create a blank solution but how do I create a blank project in which to host this?<<

I'm sorry, I should have explained.  This is important enough that I'll answer in a new blog post.

Thanks!

# May 31, 2008 8:48 PM

nexus888 said:

I tried to change your example to assign the pages to the visualroot element of the App class, but it didn't work. Instead it would keep pointing to the initial page that was assigned to it.

Would you know why this is the case?

# June 1, 2008 3:32 AM

jesseliberty said:

Yes, the documentation points out that you can only assign to the visual root once.

-j

# June 1, 2008 7:46 AM

Dew Drop - June 1, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - June 1, 2008 | Alvin Ashcraft's Morning Dew

# June 1, 2008 9:59 AM

Blogs said:

I posted the code for the PageSwitcher app described in a previous blog post , but to save space and

# June 2, 2008 12:56 AM

Weekly Links: ASP.NET MVC, .NET, ADO.NET Data Services … said:

Pingback from  Weekly Links: ASP.NET MVC, .NET, ADO.NET Data Services &#8230;

# June 2, 2008 4:45 PM

Microsoft Weblogs said:

Pages as a paradigm likely pre-date the web itself although they have been popularized by document-based

# June 8, 2008 10:05 PM

Suprotim Agarwal said:

Pingback from :

How to Navigate and Pass Values between Pages using Silverlight 2 Beta 2

at

www.dotnetcurry.com/ShowArticle.aspx

# June 25, 2008 6:02 AM

matt_stephens said:

Pages aren't garbage collected!

I just created a small sample based on this to test that the garbage collector does reclaim the page but it appears not to until the browser is closed.

I added a finalizer to one of my pages and put a breakpoint in it. On one of the other pages i call GC.Collect() after calling the Navigate method. No matter how many times i cycle round different pages i never see the finalizer run but as soon as i exit the browser the finalizer runs for every instance of the page that i created.

In Ashish Shetty's article all pages are created up front and i wonder if this is the reason? We want to write an application that could potentially be running for a very long time so to know that the garbage collector is working properly and we're not going to consume loads of memory is very important to us.

# June 30, 2008 7:34 AM

jesseliberty said:

Matt, you are absolutely correct. After talking with Ashish (who is truly a generous and smart person) and revisiting my code:

1. The way I am doing it is by having references in each page to the other, (much like WPF’s navigation model, the “navigate by object” pattern) which does allow for creating retrievable state but is not what I intended.  

2. There is an obvious alternative (using "navigate by reference") that I will demonstrate in an upcoming video (a few weeks).

Thanks for pointing this out.

# June 30, 2008 11:31 AM

matt_stephens said:

Could you elaborate on what you mean by 'navigate by reference'?

I never downloaded your code, only read the article and implemented a quick test. I am not maintaining any references from any of my page classes to the ones i navigate to so i can only assume it's the underlying handling of objects within silverlight that is stopping my 'pages' from getting garbage collected.

Thanks for any help. Look forward to the video.

# June 30, 2008 12:40 PM

Michael Sync » 10 Silverlight 2 gotchas resolved: By Antoni Dol said:

Pingback from  Michael Sync  &raquo; 10 Silverlight 2 gotchas resolved: By Antoni Dol

# July 19, 2008 3:20 AM