Silverlight 3 GDR 1 (3.0.40723.0) Taming the treeview

Breaking changes in Silverlight's navigation framework

Published on Tuesday, July 14, 2009 11:08:00 AM UTC in Programming

Dieses Post stammt aus dem urspr√ľnglichen "Mister Goodcat"-Blog bei Geeks with Blogs (2009-2010).

Today I was working with a small Silverlight application that is using the new navigation feature. Basically I adapted what Tim Heuer (thank you for your great videos and posts!) shows in his tutorial here. Apparently that sample has not been updated for the final version of Silverlight 3. When you watch that video and download the sample code, you'll notice that although it will compile under Silverlight 3 RTM, running it will result in a white screen. In Firefox that is -- curiously enough IE partly shows the sample, but it's not working. Unfortunately, you don't receive any runtime exceptions, even with the debugger attached. In the error console however, the following Java Script exception is logged:

Error: Unhandled Error in Silverlight Application

Code: 4004

Category: ManagedRuntimeError

Message: System.ArgumentException: Navigation is only supported to relative URIs that are fragments, or begin with '/', or which contain ';component/'.

Parameter name: uri

[...]

So the first thing is to change all the URIs used in the sample and add a preceding slash to them. In particular, you need to change the Tag properties of the HyperlinkButtons in MainPage.xaml, the parameter of the call to Navigations.Navigate(...) in CustomerList.xaml.cs, and of course the mappings defined in the resources of App.xaml (hint: you could also use the NavigateUri property of the hyperlink buttons instead of Tim's approach, which makes the sample even easier).

<navcore:UriMapper x:Key="uriMapper">
    <navcore:UriMapping Uri="/Home" MappedUri="/Views/HomePage.xaml" />
    <navcore:UriMapping Uri="/About-Us" MappedUri="/Views/AboutPage.xaml" />
    <navcore:UriMapping Uri="/Customers" MappedUri="/Views/CustomerList.xaml" />
    <navcore:UriMapping Uri="/Customer/{id}"
        MappedUri="/Views/CustomerDetail.xaml?id={id}" />
</navcore:UriMapper>

(Precede all occurrences of the URIs with a slash, exemplarily shown for the UriMapper here)

After that, you'll receive a different Java Script exception:

Error: Unhandled Error in Silverlight Application No XAML was found at the location '/Home'. [...]

The reason for that is a bit more tricky to find out. In the beta version of Silverlight 3, it was sufficient to provide an UriMapper with a certain key (e.g. in the App's resources), and that magically was used throughout the application. Silverlight 3 RTM requires the UriMapper property of a navigation frame to be set explicitly. You can bind that property (in MainPage.xaml) to the static resource defined in App.xaml:

<navigation:Frame x:Name="MainFrame" UriMapper="{StaticResource uriMapper}"

Now the application basically is working: you can navigate through the pages, and all content is displayed correctly. You can also use the corresponding methods to go back and forward programmatically. However, you'll notice that the navigation buttons of the browser won't work. The address bar won't be updated also, and neither will deep linking work. Why is that? Well, it took me four hours (shame on me?) to find out why, and that evil little detail is the reason I've decided to write this post after all.

I started reading through the breaking changes document, but I couldn't find anything helpful regarding my problem. I played around with some of the Frame's properties, like "JournalOwnership", which sounded promising but didn't change anything either. Of course I tried searching the web, but apparently few or no people ran into this issue. Then I tried a different approach and built a new Silverlight navigation application (Visual Studio has this nice template...) from scratch. Of course it worked like a charm. And I couldn't see anything that was done differently from my own application. But this template already is pretty sophisticated, using styling and all, so I began to take out all the unneeded stuff until nothing more than maybe 20 lines of code were left. Basically two hyperlink buttons, the frame, and two pages, consisting of nothing more than text blocks. And still it worked, whereas my own, equally pared-down application did not. In desperation, I started hosting the application in IIS (no difference), and then finally started the Silverlight project directly (instead of using the development server) - and BAM! It worked.

This gave me the final push I needed. There must be something wrong with the generated page, because when you start the Silverlight application directly, it dynamically generates a temporary HTML page, which obviously was different in some detail from the HTML page of the web project in the sample code. And then I saw it: the IFrame used for the history had an id attribute set, whereas the HTML page from the sample had not. I added

<iframe id="_sl_historyFrame" ...

to the IFrame tag and the sample worked instantly...

This is one of the bad examples I'll remember for the next years, where a small hint in a release notes document or similar would have saved me hours of frustration. I also hope Tim will update his tutorial soon because probably others will run into this issue too, especially because his sample is not marked as outdated or for SL3 beta only.

Tags: Geeks With Blogs · Silverlight