How to build huge dynamically loading cross platform Silverlight Business Applications

Standard

Embedding RIAs in your web pages can be a rewarding experience

The Silverlight 3 navigation framework provides a really nice way of integrating with the browser history.  By default it tends to support a single silverlight control model rather than leveraging the full power of asp.net.  In this article I propose an way of structuring applications that utilise asp.net and Silverlight.   The approach even allows the use of HTML, WPF-XBAP and Flash as an alternative to Silverlight or in any combination. 

 

Out of the box with Silverlight 3

It seems to me that the Silverlight 3 navigation system works best as a single control embedded in a page (contained in a single xap file).  Navigation (and loading of the many parts of an application) happen within that single control.  This is similar to the traditional “smart client” architecture.

The Silverlight 3 navigation samples provided allow the straight forward creation of this kind of smart client, with a nice Silverlight themeable container.  For simple applications, I recommend people take a look.

To take this even further the Prism framework (which I have found integrates well with Silverlight 3) provides and excellent container framework for those who would like to take this model further.

image

 

Dynamically loaded Assemblies in Silverlight 3

In the article “Silverlight 3 Navigation: Navigating to Pages in dynamically-loaded assemblies” , David Poll shows a way of dynamically loading pages into the primary navigation container by using “shim” pages within it. 

David describes his strategy as follows:

  • 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

For a developer, this means creating empty shim pages in the main project for every page they require in other assemblies.

As admitted by the author, this solution is “it’s a bit obtuse, and still harder to do than it ought to be”.  I believe David has pushed the Silverlight smart client model as far as it can go. 

If you read through the comments section of David’s article “mitkodi” discusses a way of using UriMapping to simplify the solution.  This is similar to the approach I’m proposing in this article.  (I didn’t see this before)

Perhaps Microsoft will create some markup of some kind to allow cross navigation between assemblies.  I hope they do. 

 

Another Way

It seems to me that there are two ways RIA applications can be built, either as a smart client (as described above) or as true web application web pages, each with embedded controls. 

In the second approach, each Silverlight page could be embedded on it’s own web page.  Navigation could happen at the page level, leveraging the natural navigability of web solutions.    The web page could act as a simple “shim” for hosting the RIA content, or it could provide more such as cross-platform and “deep linking”.

 

image

 

In short you would have a solution that is:

  • Navigable : Navigation could work without the Navigation framework
  • Cross Platform : An alternate web rendering of it’s content could be provided (which also allows for simpler “deep linking” for SEO)
  • Modular : If each Silverlight page was on it’s own webpage, each page could have it’s own xap file, with no special workarounds or compromises
  • Versatile: You can use any Silverlight 3 features you like (including RIA Services for example), using as many or as few assemblies you wish
  • Not Silverlight Specific : The solution could work for any embedded RIA technology, including Flash (Flex) or WPF-XBAP.

 

I’ve always thought that combining existing web technologies (DHTML, AJAX, Server side rendering) with RIA would provide something far greater than just an RIA or a web application on their own.

In the the RIA Hybrid example shown in this article, Routing support (introduced in .NET 3.5 SP1) and master pages are used to make the solution simpler.

 

The RIA Hybrid

 

Write a letter Maria, address it to Silverlight...

 

In this example, Maria the customer begins at a “home” page, then navigates from this page to a contact enquiry, then to a contact detail.

image

The Solution

To the Maria it’s just a normal website with normal navigation.  They can use browser history and bookmarking work regardless of the browser they are using.  Here’s what the solution looks like:

 

The solution

The content Maria sees is actually implemented entirely in Silverlight.  In web page“Home.aspx” is a simple web page that provides an alternate web rendering of it’s content if Silverlight is not available.

 

image

 

Here’s the layout xaml of Home.Mainpage,xaml:

  <Grid x:Name="LayoutRoot">
        <StackPanel>
             <TextBlock FontSize="100" Text="Welcome to Home" />
            <HyperlinkButton  x:Name="lnkViewContactEnquiry"  Content="View Contact Enquiry" Tag="ContactEnquiry.aspx" />
        </StackPanel>
        </Grid>
</UserControl>

Here’s the code of Home.Mainpage.vb:

Private Sub lnkViewContactEnquiry_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles lnkViewContactEnquiry.Click
    System.Windows.Browser.HtmlPage.Window.Navigate(New Uri(sender.Tag, UriKind.Relative))
End Sub

 

As you can see, the code is quite simple and provides an almost html-like approach to navigation.

The code for Home.aspx is also straight forward:

<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/Container.Master" CodeBehind="Home.aspx.vb" Inherits="WebHybrid.Web.Home" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">

     <h1>Welcome to Home</h1>  
    <asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="ContactEnquiry.aspx" >View Contact Enquiry</asp:HyperLink>
</asp:Content>

As you can see, the asp.net page only contains simple html.   This means that the developer only has to concentrate on providing the cross-platform rendering of the content.   So where’s the Silverlight?

I’ve stored the Silverlight magic in a master page (“Container.Master”) which can be reused with other Shim pages.  Basically it checks to see if a matching xbap file exists for the page, and if it does it will render this in a Silverlight host instead of the “ContentPlaceHolder1” web content.

You may notice, that in either case, Maria navigates successfully to ContactEnquiry.aspx.  If you look at the solution screenshot above you’ll notice there is not ContactEnquiry.aspx.

 

The Phantom ContactEnquiry.aspx – Routing

If the developer doesn’t need to provide web content, the cool new .NET3.5 SP1 “Routing” feature comes to the rescue.   This feature allows redirection to a generic hosting page if the called aspx doesn’t exist.  In this case, GenericSilverlightHost.aspx. 

 

The magic of redirection is accomplished by creating a “Route Handler” class.  I haven’t seen any vb.net examples of these on the web, so here’s the code:

Imports System.Web.Routing
Imports System.Web
Imports System.Web.UI
Imports System.Web.Compilation
Imports System.IO
Public Class WebFormRouteHander
    Implements IRouteHandler
    Private msWebFolder As String
    Public Sub New(ByVal path As String)
        msWebFolder = path
    End Sub
    Public Function GetHttpHandler(ByVal requestContext As System.Web.Routing.RequestContext) As System.Web.IHttpHandler Implements System.Web.Routing.IRouteHandler.GetHttpHandler

        Dim lsPage As String = requestContext.RouteData.GetRequiredString("Page") & ".aspx"

        Dim lsFullPathOfPage As String = Path.Combine(msWebFolder, lsPage)
        If File.Exists(lsFullPathOfPage) Then
            Return BuildManager.CreateInstanceFromVirtualPath("~/" & lsPage, GetType(Page))
        Else
            Return BuildManager.CreateInstanceFromVirtualPath("~/GenericSilverlightHost.aspx", GetType(Page))
        End If

    End Function
End Class

It’s wired up with this code in Global.asax:

Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
    ‘ Fires when the application is started
    RouteTable.Routes.Add(New Route("{Page}.aspx", New WebFormRouteHander(Server.MapPath("/"))))
End Sub

Please remember that to get Routing working on your site you will need to modify your web.config file.  The easiest way is to search for the word “Routing” in the sample web.config.

So that’s it!

Download the code now and try out the solution, I think you’ll like it.

 

Conclusion

So developers can create an unlimited number of Silverlight pages, in any number of dynamically loaded xbap assemblies and provide navigation between these.

This rather obvious solution could be taken much further. It could work with other web frameworks such as ASP.Net-MVC or a completely non-Microsoft one.  It could work with any RIA technology or combinations of those.

I’m really interested in what people have to say about this approach.

 

Download the Code

Links

Silverlight 3 Navigation: Navigating to Pages in dynamically-loaded assemblies – David Poll

Goodbye to Url Rewriting

 

Share this post :

Advertisements

10 responses »

  1. Hiya! This is David Poll (yep, the one mentioned in your post :)). This is a very interesting idea that’s worth exploring. Like each of the other proposals you discussed, it has its benefits and drawbacks. Here are some of my thoughts (just my humble opinion):

    What’s nice:
    -I already know how to navigate to other web pages, and that boundary is pretty well understood for me as a web developer
    -My application can be partitioned across multiple pages (or even multiple silverlight controls/xaps on the same page) without needing to understand assembly loading or dependencies, since the tools take care of that for me.
    -Authentication need not be silverlight-based. I can use standard auth functionality from ASP.NET to restrict access to the particular Silverlight page
    -This may make SEO considerably easier, since I can have each of those pages generate content for spiders that relates directly to the Silverlight content. There does exist an SEO solution through .NET RIA Services and ASP.NET for Silverlight applications, but this may simplify it further.
    -URLs are considerably cleaner, since I no longer need a URL attached as a fragment to the browser URL. This will play nicer with the browser’s history under certain circumstances, too.
    -The technique is applicable to a variety of RIA technologies out there (as you mentioned), with little modification

    Potential drawbacks:
    -Passing information from page to page is troublesome, since each one is in a new Silverlight instance, and they don’t share memory. Furthermore, if you try to go back after navigating, any text entered by the user, etc., would need to be manually re-populated, and if there was any state to the Page that should remain when you return, you’ll need to cache this to Isolated Storage
    -Duplication of downloads is really easy to encounter and difficult to avoid for custom libraries — if I have two XAPs, each of which includes System.Windows.Controls.Data.dll (a big library containing the DataGrid), I’ll end up downloading that twice. Across multiple pages, this gets to be a pain unless you use the SL3 assembly caching feature, which requires some extra developer effort for any shared class libraries
    -Isolated storage app settings aren’t shared (because they’re separated by XAP URL)
    -I do have to wait for the SL runtime to load up and XAPs to download with each page change
    -Internal navigation within the page isn’t really possible, since I have to change to a brand new web page every time. With the dynamically-loaded assembly model, you can have multiple Frames, each navigating to different pages, showing different pieces that have been downloaded on-demand piece by piece
    -I’m forced to basically have 1 Silverlight project per page

    A quick idea that might improve this approach:
    -Use a single xap for all of the pages, just change which page the xap loads at startup based on initparams. This way, you only need to download that xap once (the browser will cache it from page to page). If you build in a little infrastructure to download the real content you want dynamically, you allow them to share App settings through Isolated Storage, and you can cache the other libraries as you see fit (perhaps allowing you to avoid double-downloading, etc.).

    Personally, I find this area very interesting to explore. As you can probably see in my latest blog post, I’m looking even further at ways to simplify the process of building large apps in this way (in fact, one of my ideas is precisely what you suggested, allowing developers to navigate to a page within a XAP based solely on the URI). There are a variety of ways to accomplish all of this, and I look forward to seeing what you come up with as you explore this model further! I’ll definitely be watching your blog 🙂

    • Thanks for the feedback David. I appreciate someone taking the time to truly think through the ramifications of a solution like this.

      Thankyou also for the idea about using a single xap file. I mentioned in the article that this was possible, but I wanted to keep the sample as simple as I could. Perhaps the next one will say more. Url routing also provides some very interesting opportunities along these lines.

      My comments on the drawbacks:

      -Passing information from page to page is troublesome, since each one is in a new Silverlight instance, and they don’t share memory. Furthermore, if you try to go back after navigating, any text entered by the user, etc., would need to be manually re-populated, and if there was any state to the Page that should remain when you return, you’ll need to cache this to Isolated Storage

      You are correct. I found this can be a problem with Silverlight 3 navigation and WPF navigation. My prefered solution is to pass data via query strings, which is also bookmark friendly.
      Isolated Storage seems like a reasonable approach to the problem also.
      Looking “outside the box”, it would be possible to utilise cookies, google gears (with sql lite) or even the state management features of ASP.Net to store state.

      -Duplication of downloads is really easy to encounter and difficult to avoid for custom libraries — if I have two XAPs, each of which includes System.Windows.Controls.Data.dll (a big library containing the DataGrid), I’ll end up downloading that twice. Across multiple pages, this gets to be a pain unless you use the SL3 assembly caching feature, which requires some extra developer effort for any shared class libraries

      I haven’t researched this yet. I was hoping that SL3 assembly caching would “save me”. Certainly a topic for a future blog post as I progress.

      -Isolated storage app settings aren’t shared (because they’re separated by XAP URL)

      Hmmmmmm…true…I hate that

      -I do have to wait for the SL runtime to load up and XAPs to download with each page change

      In my limited testing this doesn’t seem to have a significant impact. The main hit is the initial load (disk io) of the Silverlight runtime on the first page.

      I left out another advantage in the orginal post, and that is immediate responsiveness when accessing the application for the first time because of the use of HTML in the page.

      I intend to generate large simulated applications to test this further.

      -Internal navigation within the page isn’t really possible, since I have to change to a brand new web page every time. With the dynamically-loaded assembly model, you can have multiple Frames, each navigating to different pages, showing different pieces that have been downloaded on-demand piece by piece

      Good point. Although a hybrid application would probably use html iframes for this kind of functionality.

      -I’m forced to basically have 1 Silverlight project per page

      In this sample that’s the case, because I’ve intentionally simplified it. Conceptually I intended more than one page per xap.

  2. Pingback: Silverlight 3 Navigation: Navigating to Pages in dynamically-loaded assemblies « davidpoll.com

  3. Code is not running in my case, giving error
    Type ‘RiaContext’ is not defined. D:\Personal\Downloads\SilverLight 3 Stuff\WebHybrid\Home\MainPage.xaml.vb 3 37 Home

    although its really a awesome idea, I Love It.

    Thanks again,
    Niaz

    • Hi Niaz,

      I inadvertently added a reference to the July RIAServices preview. Sorry.
      To get the sample working you could dereference RIAServices and remove any classes that refer to it.
      I’ll remove this from the next sample I post.

      Regards
      Julian

  4. Pingback: RIAServices – Proudly using SessionState within your DomainServices « An Original Idea

  5. Good article. I’m working on large Silverlight app. What I’ve decided to do is divide the Silverlight app into a number of subsystems with each subsystem hosted in an asp page. Each subsystem is comprised of one or more closely related Silverlight pages.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s