Non-Linear Navigation in Silverlight 4

In the last Seattle Silverlight User Group meeting I spoke about Non-Linear Navigation in Silverlight. 

This blog post is a proof-of-concept for Non-Linear Navigation in Silverlight 4 Navigation Applications.  When I was first exploring this, I did not use the Navigation Application template, instead I implemented Non-Linear Navigation similar to what I did in BBQ Shack.  Several friends asked me to try and use the Navigation Application template so I did.

The code in this post builds on any of the Navigation Application templates.  For this post, I’m using the default template, but there are other cooler ones in the Visual Studio Code Gallery.

What is Non-Linear Navigation?

I first heard about Non-Linear Navigation in this dnrTV video that Billy Hollis did.  While on a cruise to Alaska I wrote the BBQ Shack application, which is a WPF implementation of Non-Linear Navigation.

Non-Linear Navigation is one way for Silverlight line of business applications to provide users with a desktop application like experience.  This would include both in-browser and out-of-browser applications.

The primary problem that is being addressed in this blog post is how to allow users to easily switch between unfinished tasks and for them to visually see what tasks they started but then navigated away from.

For example, the user is adding a new item to the system.  Their boss calls them to look up history of another item.  The user does not want to quit the add new item task to look up history of another item.  In your current Silverlight applications, how would you handle this scenario?

Non-Linear Navigation easily enables this scenario, by allowing the user to navigate away from the task and start a new task.  The original task is moved off screen but not destroyed and the task information is made visible on the UI.

In the below image, the user was working on an Inventory task, then switched to another task; in this case the About page.  Notice the UI has clearly indicated to the user that they have one active Inventory task.

about

In the below image the user has just clicked the Inventory button at the top right and navigated to the Inventory module home page.  Notice the three visual clues presented to the user.

  • Top right – Inventory- (1) number is the total number of Inventory tasks open
  • Left side – Item – (1) number is the total number of Item tasks open
  • Right side – the active item is displayed in a list with a link to return to the task

inventory

The information surfaced about open tasks on the UI comes from a collection.  This makes it very easy to have any sort of UI your designer/deviner/developer can dream up.

How is this Implemented?

When you want to provide custom page loading in a Navigation template you’ll want to implement a Frame.ContentLoader.

David Poll is the grand master of Silverlight Navigation template and custom ContentLoaders.  David is the Silverlight Program Manager for Navigation and has written a lot about navigation.  You can read all of David’s Navigation posts here

David and I met several times to discuss navigation and the problem space I was exploring.  He provided me super guidance on navigation in Silverlight which I appreciate very much.  Thank you David!

The below XAML is familiar to Silverlight developers who have used one of the Navigation templates.

When the Frame receives a request to Navigate, it passes the heavy lifting off to the ContentLoader.

The default Navigation template does not specify a ContentLoader.  If a ContentLoader is not specified, the default ContentLoader will be used.  In the below XAML you can see where I have added the NonLinearNavigationContentLoader.

ContentLoaders are super cool in that you can chain them.  The commented out below XAML shows how to chain ContentLoaders.  The chaining of ContentLoaders allow each ContentLoader to perform an action, conclude the processing of the Navigate request or defer further processing to the next ContentLoader.

David Poll has written a lot about chaining ContentLoaders so I won’t attempt to duplicate his work in this space.

<navigation:Frame 
    x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" 
    Source="/HomeView" Navigated="ContentFrame_Navigated" 
    NavigationFailed="ContentFrame_NavigationFailed">
    <navigation:Frame.ContentLoader>
        <!--This is where our custom Content load is added-->
        <navigatus_Navigation:NonLinearNavigationContentLoader>
            <!--
                                                    
            <navigatus_Navigation:NonLinearNavigationContentLoader.ContentLoader>
                <local:YouCanChainAnotherContentLoaderHere />
            </navigatus_Navigation:NonLinearNavigationContentLoader.ContentLoader>
            -->
        </navigatus_Navigation:NonLinearNavigationContentLoader>
    </navigation:Frame.ContentLoader>
</navigation:Frame>

Simply stated, the NonLinearNavigation ContentLoader does the following when the Frame receives a Navigate request:

  • Checks if the requested Uri is in the collection of active tasks
  • If not in the collection it will:
    • Defers loading to the default ContentLoader (or chained ContentLoader)
    • After content is loaded, it attaches a NavigateKey to the NonLinearNavigationContentLoader.NavigateKey attached property.
  • If in the collection it will:
    • Return the object matching the Uri to the Frame, no further processing is performed.

Kind of simple, uh?

For the MVVM folks out there, you’ll be happy that a Navigation can easily notify your ViewModel when the View is being brought into view or when its being navigated away from.

The PageBase class does implements this automatically for you.  In some cases a Page may want to override the behavior.

The ViewModelBase class handles some plumbing that deriving classes can override to provide context specific actions.

In the below code snippet from the ItemMaintenanceViewModel class, we can see two overridden methods.

OnNavigatedTo either instantiates a new record or calls the business layer to load a record.  Notice how it first checks to see if it already has data loaded.  This would be the case when navigating back to an active task.

OnNavigatingFrom fulfills two important purposes.  First notice the cancel parameter; this allows the ViewModel to opportunity to cancel a Navigation request.  Second, this method populates the attached NavigateKey with the latest form data.  There are many different techniques you could use to populate the NavigateKey, I’ve chosen the simplest method I could think of to illustrate concepts.

public override void OnNavigatedTo(Object param) {
    if (this.Item == null) {
        if (param is Int32) {
            Int32 id = (Int32)param;
            if (id == 0) {
                this.Item = new Item();
            } else {
                this.Item = InventoryBLL.GetItem(id);
                if (this.Item == null) {
                    throw new Exception(Constants.RECORD_NOT_FOUND);
                }
            }
        }
    }
}

public override void OnNavigatingFrom(ref Boolean cancel) {
    if (base.NavigateKey != null && this.Item != null) {
        if (this.Item.ItemID == 0) {
            base.NavigateKey.Mode = Constants.ADDING;
        } else {
            base.NavigateKey.Mode = Constants.EDITING;
        }
        base.NavigateKey.Key = this.Item.ItemID.ToString();
        base.NavigateKey.Title = this.Item.Description;
        base.NavigateKey.IsChainable = true;
        base.NavigateKey.ApplicationSuite = Constants.APPLICATION_INVENTORY;

        //NavigateKey.CurrentSource is set in PageBase as it requires access 
        //to the Navigation Service
    }
}

Navigatus Application

I’ve broken the Navigatus Application into several assemblies to demonstrate how to have a Shell Page that hosts the application content.  The actual application content comes from satellite assemblies.

Check out the About page.  When you navigate to it, a counter will start incrementing.  After a bit, navigate back to it, you’ll see that the counter continued to work even though the page was out of view.

The techniques discussed here open up many possibilities for Silverlight 4 line of business applications.

Download

Navigatus C# Source Code

I’ve been programming in C# since August of 2009.  Most of my work from here forward will be done in C#.  For my VB.NET brothers in arms, Karl has moved on to the dark side.  Feel free to send me an email or comment if I need to clear up some C# code.  Except for { } or ; it’s pretty much the same.

I’ve move to C so that I can pickup Objective-C and C++.

Close

I hope you can learn something from this proof-of-concept application.

I was hoping to have more time to work on a nice Silverlight line of business application but my non-work time is currently being consumed by my work on Mole 2010 and learning Windows Phone 7.  In my spare time I’m reading my first C++ book.  Once Mole 2010 ships, I need to finish Crank (my super cool code generation application) and then I hope to write a cool Silverlight line of business application that uses the techniques here.

I’m also hoping the community embraces navigation and develops patterns and frameworks for enabling RAD applications that empower uses of the software we write.

Have a great day,

Just a grain of sand on the worlds beaches.

21 Responses to Non-Linear Navigation in Silverlight 4

  1. Non-Linear Navigation in Silverlight 4 – Karl On WPF…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  2. goldytech says:

    Karl,
    You simply ROCKS. Great content once again. Learned lot about navigation. I was wondering whether something similiar to BBQ Shack WPF app can be done in Silverlight , where you had to do a right click on the icons and a pop up comes to select any open screens. I assume you must have demonstrated the same in your talk in Seattle Silverlight User Group. If you don’t mind can you please share that sample code with us. Also you can also look at http://www.clientui.com , they have awesome navigations controls.

  3. leblancmeneses says:

    [quote]
    I need to finish Crank (my super cool code generation application)
    [/quote]

    any details on “Crank” ship date and what is planned in beta release?

  4. I “hope” by the end of August. “hope”.

    I don’t do beta’s just releases.

    Documentation will be the “thing” that will hold me up, not the software.

    Cheers,

    Karl

  5. joebrockhaus says:

    I know this is about navigation for the most part, but I’ve got a question that flows downhill from where this technique takes you: non-navigation-based multi-tasking (aka, windowed), and saving individual items.

    I think as a user, I would expect to be able to get a call, open up an existing record (or create a new one), edit it, then save & close it, without having to save all the other items along with it.

    A couple of different options are ‘available’ but each have their own problems.

    1) each item view gets its own ServiceContext, this way saving an item saves the whole context, but only one item exists in the context. However, this creates an issue with the rest of the app ‘knowing’ about it, and reacting accordingly.

    2) using RIA Services Contrib, copy the entity into a temporary ServiceContext, save it, then copy the updated entity back into the master context.

    The reason I’m asking is because I’m in the middle of creating an MDI-style SL4 app. It’s a migration from an MDI app. At the onset of this project I found this post exceptionally insightful and optimistic about using navigation. However, adding navigation on top of the MDI framework was just extra work that provided little benefit.

    Now I’ve hit a number of frustrating issues. The first of which is cleanly maintaining a common source to which everything binds. (This is why I’ve come back to this video to inspect how you’ve managed this). For the onset, I’m not worried so much about saving individual items as i am making all the cross-window binding work, without having to do massive amounts of eventing/messaging across all the various windows’ viewmodels.

    This seems to be complicated by the fact that most implementations/demos/best-practices seem to expect that the view will only be around once. As a result, the viewmodels always seem to be clearing and refreshing the ServiceContext entity collections on every action that requires update/save to existing entities or a new loadop. In a real-world LOB application where performance is very important, this seems like unfortunate waste, and on top of that, a lot of supporting code to abstract binding of the ServiceContext entity collections through method calls and chained INotifyPropChanged events, etc. I’m not wanting to remove the abstraction, but I feel like I’m either doing something wrong, or this is not the intended application of the technology and as such will require the seemingly extra and verbose code to make it work.

    Do you have any advice for this type of application?

    • joebrockhaus says:

      A handful of the concerns I was encountering can be solved by some of the recommendations put forth by the DevForce for Silverlight team in this link:

      DevForce Hijack: Microsoft Kung Fu MVVM (Documentation.DevForce Hijack Microsoft Kung Fu MVVM) – IdeaBlade DevForce

      http://j.mp/g8qI7u

      while it is an exercise in converting from Ria Services to DevForce, the associated .docx goes into good detail about the changes that were made, and more importantly, WHY, and the problems those solutions were addressing.

      This was really made possible by John Papa’s excellent PDC ’10 demo, but the DevForce team took it the final step toward best-practices and maintainability, clarifying along the way.

      However, I’d still be interested in hearing your thoughts on the dilemmas I encountered.

      Thanks!

    • I have not and don’t use RIA services so it would be difficult to provide good guidance for your scenario.

      I like to abstract the persistence from the views and viewmodels. My views and viewmodels do not know about the persistence or service layers and I don’t allow their implementation to leak into the view or viewmodel.

      But, that advice could be considered contrary to using RIA services they way it is demonstrated.

      I suggest writing up small applications using both techniques and see which one works for you, and then use it.

      Have a nice day,

      Karl

  6. joebrockhaus says:

    oops. forgot to set get follow-up comments.

  7. gcadmes says:

    Hi Karl,

    Thanks for this excellent post!

    Quick question, In the Silverlight TV show, you mentioned something about a macro you like use to place the ViewModel’s cs files beneath the View’s. Is the macro in the navigatus download? If not, can you kindly instruct me on how to physically relocate my ViewModel files?

    thanks much,

    Greg

  8. marshallstickley says:

    Karl,

    I’m new to the whole WPF, Silverlight, MVVM, EF4 world, although not new to VB or .NET. I work in a corporate shop developing custom applications to support the business. Currently we are using winforms and vb. net 2005. We are looking hard at Silverlight, VB 2010, and WCF Ria Services. I have been tasked with researching the navigation aspect for our Framework that our developers will use for building applications.

    I am very interested in the non-linear navigation sample you put together, my question is have you combined it with the Business Application template or do you know of a sample that does this. I am becoming very adept at deciphering the C# code so language isn’t that important. I’m just trying to get my hands on a comprehensive sample that I can use to gain some traction as the project we have targeted for this is on a fairly tight timeframe and I’m anxious to get ahead of the curve here.

    Any advice or assistance would be greatly appreciated.

    • Welcome to the XAML world. Nice place to be.

      Does your application require Silvelight navigation and the deep linking feature?

      I would recommend looking Prism: http://compositewpf.codeplex.com/ The MSDN documentation includes a book that talks about many topics that will interest you.

      I have written several Prism navigation articles here: http://blogs.msdn.com/b/kashiffl/ One of the articles explains how to use Prism with Silverlight Frame navigation.

      Where are you located? I’m speaking about Prism in several code camps, Boise and Dallas in Feb & Mar. Charlotte in May.

      We are also looking to do several 2 day training events.

      Cheers,

      Karl

  9. goldytech says:

    Hi Karl

    This looks good, but i have one peculiar problem in my project. I need to open the link in the new browser tab. Let me give you my scenario. I have home page with the icons of all the modules (Orders,Customers etc). In this icon there is a check box on the right hand conrner, when this check box is checked that module should open in a new tab of the browser. Can you provide some guidance on this.

    Thanks in advance.

    Best,
    Goldy

    • Karl says:

      Umm.. I would try and avoid opening a second browser tab. Keeping the user in a single browser tab and window may make it easier for them and won’t lead them down the old MDI pile of windows problems that non-linear navigation solves.

      How the browser navigates when the user clicks a hyperlink is controlled by their browser settings and keyboard. For me, its CTRL+click to open in a new tab.

      I don’t know of a way to programatically open a new tab, new window, yes, but now new tab.

      Karl

Follow

Get every new post delivered to your Inbox.

Join 247 other followers