davidpoll.com
Posts Tagged Silverlight 3
Silverlight 3 Navigation: Navigating to Pages in dynamically-loaded assemblies
Posted by david.poll in Silverlight on July 14, 2009
In my last post on the new navigation feature in Silverlight, I explored how you can navigate to pages in assemblies/projects referenced by your main application. Almost immediately, a number of you asked how you can navigate to pages in assemblies that have been dynamically loaded, allowing you to delay downloading certain pieces of your application until users request them. This allows you to reduce the initial size (and thereby load/download time) of your application to something more appropriate for your users and avoid using bandwidth to download components to users’ machines that they may never actually user/encounter.
Out of the box, Silverlight 3’s navigation feature only allows navigating to Pages in assemblies referenced by your application project and packaged in your XAP. There are a number of technical reasons behind this that I know we’d like to explore and hopefully address in future versions, but I won’t go into the details here. There is, however, one exception to this rule that prevents us from navigating to Pages in dynamically-loaded assemblies: you can load Pages with no code behind that live in such assemblies.
After a few months of thinking about this problem in the back of my mind, it finally clicked for me: this exception is the way in! All of the other workarounds I was able to think of required Pages located in the XAP that would load their contents dynamically and didn’t really take advantage of the Navigation feature at all.
Anyway, I think I finally found a reasonably good workaround, and while it’s a little hacky-er than I would’ve liked, it doesn’t fundamentally change the feel of using Silverlight navigation. Here’s the gist of my strategy:
- Load an assembly containing a Page dynamically
- Include in this assembly a “shim” Page with no code behind. In its XAML, reference the real page with code behind, and forward any navigation-related information (events, title, query strings, etc.) to that Page
- When that page is navigated to, replace the Frame’s content with the real page.
- From the application, navigate to the “shim” page in the dynamically-loaded assembly
Ok, I know that sounds a little roundabout, but I think I’ve made it pretty easy to accomplish for a developer now. More importantly – it works!
Let me go into a little more detail…
The Library
I’ve created a tiny little assembly that I’ve called “DynamicNavigation”. It contains two classes, both of which are made for use in XAML:
- DynamicPage: A descendant of the Page class that allows the Shim to communicate with it. If I did my job correctly, for 99% of cases, you can navigate directly to this page (rather than its Shim) if you haven’t dynamically loaded the assembly. There shouldn’t really be any new public interface (except for some “new” properties that hide the original Page properties, since they aren’t virtual).
- DynamicPageShim: Another descendant of the Page class, meant to be used in XAML with no code behind. This class will forward any navigation events, etc., to the DynamicPage it takes as its content. The goal here was to minimize the XAML so that the additional size due to these files is small. There may be further optimizations I can do, but it’s getting pretty close
The Developer Experience
Ok, that’s all great, but what do I have to do to use it?
Well, instead of 2 files (<PageName>.xaml and <PageName>.xaml.cs/vb), creating a Page now takes 3 files:
- <PageName>.dyn.xaml:
<dyn:DynamicPageShim xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:dyn="clr-namespace:DynamicNavigation;assembly=DynamicNavigation" xmlns:my="clr-namespace:DynamicallyLoadedLibrary;assembly=DynamicallyLoadedLibrary"> <my:PageName /> </dyn:DynamicPageShim>
I’ve underlined two important lines here. First, “assembly=DynamicallyLoadedLibrary” – normally, this would be unnecessary in an xmlns definition, since I can refer to the library in which the XAML file exists without the “assembly=” syntax. For the same technical reasons that we normally can’t load a Page in a dynamically-loaded assembly, this name needs to be fully specified, since it allows the XAML loader to find the dynamically-loaded assembly in which your DynamicPage resides. Next, you can see that this Shim takes an instance of the DynamicPage as its content. This allows it to forward navigation-related information to the Page and ensure that the Frame has the correct content.
- <PageName>.xaml
<dyn:DynamicPage x:Class="DynamicallyLoadedLibrary.CSDynamicPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:dyn="clr-namespace:DynamicNavigation;assembly=DynamicNavigation" xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480" Title="Page Title"> <Grid x:Name="LayoutRoot"> ... </Grid> </dyn:DynamicPage>
As you can see, there’s really nothing special about the DynamicPage except that the open/close tags are of type DynamicPage rather than Page. Gotta love it!
- <PageName>.xaml.cs/vb
I won’t show an example here, but there’s really nothing special about this. You’ll develop exactly as though the DynamicPage were a real Page. The only thing to watch out for: please don’t cast the DynamicPage to a Page, since this will cause it to refer to hidden properties that may not be set. Again, for 99% of cases, this is probably not an issue, but it bears a little bit of consideration.
The last piece of the puzzle is actually navigating to the page. Well, it’s pretty straightforward: navigate just as we did in my other post (using “;component” pack URI syntax). The only difference: instead of navigating to <PageName>.xaml, you’ll navigate to <PageName>.dyn.xaml.
And that’s it! The rest should just work!
So, without further ado, I’ve got some tools for you!
The Goods
The download above is a zip containing 3 things:
- DynamicNavigation.dll – an assembly you should reference in both your application project (and include in your xap) as well as any assemblies you wish to dynamically load (so that you can include the XAML files above)
- DynamicPageCS.zip – this is a Visual Studio item template that will help get you started. Drop this zip (without extracting) in “<My Documents>\Visual Studio 2008\Templates\Item Templates\Visual C#”. Now, if you open Visual Studio and a C# Silverlight 3 project, right-click the project (in the solution explorer) and choose “Add new…”. Select “Silverlight Dynamic Page” and choose a name for your page. The item template will create the 3 files for you. Open up “<PageName>.xaml” and start editing!
- DynamicPageVB.zip – I didn’t forget about you VB guys! Follow the same instructions above (but placing the item template in the VB folder instead of the C# one), and you should be set to go!
I’m sure there are some bugs and issues with what I did (for example, the item templates don’t add a reference to DynamicNavigation.dll to your project yet… I hope to fix this once I figure out how!). Let me know if something’s causing you problems, and I’ll see if I can work it out in my (not so) copious free time
The Sample
You know me – I can’t leave you without a live sample of this thing in action! Take a look here (for your viewing pleasure):
Enjoy! You may even find a cameo from a previous post in there!
The Tips
When using my DynamicNavigation tools, there are a few things you should keep in mind. Hopefully this list will get smaller going forward, but in the meantime, these are some things to remember:
- It’s still up to you to load the assemblies dynamically into your AppDomain before attempting to navigate to them. A super-simple example of how to do this can be found in the code-behind of my MainPage.xaml. You can also use tools like Prism to accomplish something similar. There are definitely some tricks that would make this feel more on-demand than what I’ve done in the sample, but that’s a topic for a future post!
- Along those lines, please make sure any assemblies your dynamically-loaded assembly is dependent upon are loaded before trying to navigate. I don’t do anything to try to resolve those dependencies.
- Try to avoid casting your DynamicPage to its parent type – Page. If you do, you’ll find some properties aren’t set to your desired values (such as Title, NavigationCacheMode, etc.).
- My libraries should replace Frame’s Content with the intended page, so feel free to bind into Frame.Content just as you would otherwise. You may find that Frame.Content does change twice during a navigation rather than just once, but otherwise, you should get the same events/virtual methods called/etc. on your Page.
- Expect Caching and Title to still be honored properly on your page!
- Once you’ve created your <PageName>.dyn.xaml file, you should rarely, if ever, need to modify it. It’s just a shim, and any other work you’d like to do should be possible directly through the DynamicPage files (.xaml and .xaml.cs/vb)
I tried to keep the library as small as possible. Anything I build on top of this, I intend to put in a separate library, so that you’re not forced to take additional overhead for the simplest of cases, where you’ve already done your dynamic loading and just want to navigate to pages in those libraries.
The Conclusion
My hope is that this will get you started down the road to navigating to dynamically-loaded assemblies. I know it’s a bit obtuse, and still harder to do than it ought to be, but what I’d like to do going forward is take this development and create an SL3 library that will make it really simple to download assemblies from your server on-demand during navigation (and perhaps devise some other nifty features).
Please, let me know what you think. Should I spend more time on these things? How valuable is using navigation in this way to you? What kinds of scenarios would you still like to see enabled?
If I’ve confused the heck out of you, I sincerely apologize. I know I found myself struggling to describe this adequately, so please, ask questions! I’m happy to share my thoughts/experiences.
Comment away!
P.S. I’m finally on Twitter (at least partially to my dismay
). You can find me at @depoll
Update: Julian Biddle has posted a wholly different strategy that would allow you to divide your Silverlight application across multiple XAPs on his blog: http://anoriginalidea.wordpress.com/2009/07/22/how-to-build-huge-dynamic-cross-platform-silverlight-business-applications/. Take a look – it’s an interesting read/approach
Silverlight 3 Navigation: Navigating to Pages in referenced assemblies
Posted by david.poll in Silverlight on July 12, 2009
At long last, Silverlight 3 has arrived! That, in and of itself, is worth a thousand blog posts, but since there’s a plethora of folks out there doing precisely that as I type, I’ll start right out with some content on one of the new features of Silverlight 3 – Navigation.
In this post, I’ll look at some basic navigation functionality in Silverlight 3. Silverlight 3 navigation is based on navigating a Frame control to a particular Page control. On top of allowing non-linear navigation throughout your Silverlight application, the Frame control will integrate with your browser’s history and address bar, allowing you to provide deep-links to Pages within your application and provide a more web-like experience within Silverlight applications.
Using Pages and Navigation in this way allows you to structure your application much like you would a web page, with various pages providing differing functionality. Like web pages, Silverlight 3 navigation allows you to pass data through query strings, perform fragment navigation, and so on – great topics for another post
Basic navigation to pages in your main (application) project is simple – HyperlinkButtons can target a Frame, and specify the path to the Page’s XAML file, like so:
<navigation:Frame x:Name="ContentFrame" Source="/Views/Home.xaml"> </navigation:Frame> <StackPanel> <HyperlinkButton NavigateUri="/Views/Home.xaml" TargetName="ContentFrame" Content="home"/> <HyperlinkButton NavigateUri="/Views/About.xaml" TargetName="ContentFrame" Content="about"/> </StackPanel>
In this case, my Pages are “Home” and “About”, located in a Views folder within my Silverlight Application project. Clicking on the links results in changes to the address bar in my web browser, as you can see in the image below. The path in the hyperlink is displayed after the hash mark (#) in the URL. I could send someone this link to bring them right back to the specified page in my application.
This navigation functionality is very valuable in and of itself, but what happens if I want to put my pages in a separate assembly, ripe for reuse in other applications? You’ll notice that the URIs for the Pages were relative to the application project (i.e. “/Views/Home.xaml” represents “<ApplicationProject>/Views/Home.xaml”). This actually comes from the short form of the pack URI syntax in WPF (note: the full syntax is not supported in Silverlight, just a narrow subset). If you want to access Pages in referenced assemblies, you can do so using the same “short” syntax: “/<ReferencedAssemblyName>;component/<PathToPage>/<PageName>.xaml”
With this syntax, I can place (and reference) pages in a class library that will be referenced by my Silverlight application, allowing me to create a structure like the one in the image below (a simplified example, I know, but it does illustrate the point!).
For this project structure, my hyperlinks look like this:
<navigation:Frame x:Name="ContentFrame" Source="/Views/Home.xaml"> </navigation:Frame> <StackPanel x:Name="LinksStackPanel"> <HyperlinkButton NavigateUri="/Views/Home.xaml" TargetName="ContentFrame" Content="home"/> <HyperlinkButton NavigateUri="/Views/About.xaml" TargetName="ContentFrame" Content="about"/> <HyperlinkButton NavigateUri="/PageClassLibrary;component/Pages/PageInLibrary.xaml" TargetName="ContentFrame" Content="page in a class library"/> </StackPanel>
Upon running and clicking on the “page in a class library” link, you’ll notice that the address bar shows a rather long and ugly URI:
Thankfully, we’ve supplied a great feature for the Frame control that helps you deal with ungainly URIs – the UriMapper. UriMappers allow you to write code that takes a URI as input and produces a different URI as output. The output URI will be used to locate the page, while the input URI is what the user will see. In the SDK, we’ve supplied a default UriMapper that allows you to use some basic pattern matching to map URIs and keep your deep-links nice and tidy.
In my case, I’d be quite happy if I could get rid of the “/PageClassLibrary;component" that I had to add to my URI to reference Pages in a class library altogether. Instead, I’d like my URI to look like this: “/Pages/PageInLibrary.xaml”. To accomplish this, I add a UriMapper to my Frame:
<navigation:Frame Source="/Views/Home.xaml"> <navigation:Frame.UriMapper> <uriMapper:UriMapper> <uriMapper:UriMapping Uri="/Pages/{path}" MappedUri="/PageClassLibrary;component/Pages/{path}" /> </uriMapper:UriMapper> </navigation:Frame.UriMapper> </navigation:Frame> <StackPanel> <HyperlinkButton NavigateUri="/Views/Home.xaml" TargetName="ContentFrame" Content="home"/> <HyperlinkButton NavigateUri="/Views/About.xaml" TargetName="ContentFrame" Content="about"/> <HyperlinkButton NavigateUri="/PageClassLibrary;component/Pages/PageInLibrary.xaml" TargetName="ContentFrame" Content="page in a class library"/> <HyperlinkButton NavigateUri="/Pages/PageInLibrary.xaml" TargetName="ContentFrame" Content="(mapped URI) page in a class library"/> </StackPanel>
Now, users who navigate to the page are greeted with this, much “prettier” URL:
And there you have it! Dividing your pages across multiple assemblies doesn’t have to degrade user experience, and once you’re familiar with the style of URI that the navigation framework expects for such pages, it’s no more complex than standard navigation within your Silverlight application.
One last thought…
Before I leave you, I think it’s important to point something out: the Silverlight 3 SDK ships with a project template for Navigation for Visual Studio. This template makes it really easy to get started with a styleable, Navigation-enabled Silverlight application, and does quite a lot for you, including specifying some default UriMappings:
<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed"> <navigation:Frame.UriMapper> <uriMapper:UriMapper> <uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/> <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/> </uriMapper:UriMapper> </navigation:Frame.UriMapper> </navigation:Frame>
This is great – it makes the common case of having Pages within your “Views” folder in your application project really clean, allowing developers to reference “/Home” instead of “/Views/Home.xaml”. However, if you look closely at the UriMappings, you’ll notice that “/{pageName}” will match our “/<AssemblyName>;component” syntax, mapping it to something else entirely, and preventing any attempts to link directly to such URIs.
There are a number of ways to address this issue, depending on your desired URI scheme, such as:
- Delete the UriMappings entirely and go back to the full paths for all of the hyperlinks
- Modify the existing UriMappings so that the “catch-all” doesn’t match the desired syntax
- Add UriMappings for each referenced library before the catch-all mapping, like so:
<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed"> <navigation:Frame.UriMapper> <uriMapper:UriMapper> <uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/> <uriMapper:UriMapping Uri="/Pages/{path}" MappedUri="/PageClassLibrary;component/Pages/{path}" /> <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/> </uriMapper:UriMapper> </navigation:Frame.UriMapper> </navigation:Frame>
- Add a UriMapping to restore the original syntax before the catch-all mapping, like so:
<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed"> <navigation:Frame.UriMapper> <uriMapper:UriMapper> <uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/> <uriMapper:UriMapping Uri="/{assemblyName};component/{path}" MappedUri="/{assemblyName};component/{path}" /> <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/> </uriMapper:UriMapper> </navigation:Frame.UriMapper> </navigation:Frame>
This list is certainly not exhaustive, but some combination of these options is likely to help you re-enable navigation to Pages in referenced assemblies.
If you’re still having trouble getting this to work, let me know, and I’ll try to help you troubleshoot!
Last, but not least…
You thought I might leave you without source and a link to the sample, didn’t you?! Well, guess I showed you! Oh, wait, I haven’t linked them yet. Uhhh… pretend I didn’t say that
More posts will come now that Silverlight 3 is out! I’ve been waiting with bated breath! Thankfully, you folks aren’t around to suggest I use a mint
P.S. Now that Silverlight 3 is out, I’ve updated all of my samples/source to work with Silverlight 3, the July 2009 Silverlight Toolkit, and .NET RIA Services July 2009 CTP. You’ll find my Konami Code control and Activity control should both work out-of-the-box with Silverlight 3 RTM, and the rest only needed minor updates. If folks are curious about my experiences updating those projects, let me know and I’ll do a short post.
BusyIndicator, Dynamic Navigation, Navigation, Silverlight, Silverlight 3
Recent Posts
- Pitch Perfect for Android now available
- Tag Master for Android now available
- Pitch Perfect now available for free!
- Quickly building a trial mode for a Windows Phone application
- To XAML, with love (an experiment with XAML Serialization in Silverlight)
- Taking Microsoft Silverlight 4 Applications Beyond the Browser (TechEd WEB313)
- Common Navigation UI and Authorization-driven Sitemaps
- Samples updated and code in comments
Tags
.NET RIA Services Activity Control Android Authorization Barbershop Blend BusyIndicator CollectionView ContentLoader Controls Data Binding Dynamic Navigation Fun Konami Code logging MIX '10 Navigation Off-Topic Out-of-browser PDC PDC09 Personal Pitch Perfect Printing Projects Relative Links Server Silverlight Silverlight 3 Silverlight 3 Beta Silverlight 4 Silverlight 4 Beta Silverlight 4 RC Silverlight and Beyond (SLaB) Silverlight Toolkit Sitemap Tag Master TechEd TechEd North America 2010 Trusted Applications Validation Visual Studio 2010 WCF RIA Services Windows Phone 7 XAMLTwitter: @depoll
- What a great first week at the new job! It's great to feel so energized by work.February 4, 2012 7:08
- @andruwang Amen.February 3, 2012 7:50
- @andruwang @ParseIt Likewise -- it's been a fantastic first week!February 3, 2012 6:49
Disclaimer
The content on this site represents my own personal opinions and thoughts at the time of posting, and does not reflect those of my employer in any way.
Warning: Missing argument 2 for tweetable_write_widget() in /home3/depollco/public_html/davidpoll/wp-content/plugins/tweetable/tweetable.php on line 137

