Step into nRoute "Office® 2010" Demo App

Posted by Rishi on 22-May-09 8:55 AM - Comments (10)

Tags: , , | Categories: .NET, nRoute, Code

In my recent post I went through various design aspects of nRoute Containers, albeit without a single line of code - as a counter, I showed a demo specifically for Containers dubbed Office® 2010 (or more correctly Officer 2010). In this post I'll go through the Officer demo app, and like with my previous "step in" post, I'll enumerate various items in a point-by-point fashion, relating them to Container related use and design concerns.

Note even though we are aping Microsoft Office 2010 visuals, the demo app is not designed to reproduce office type functionality - it is just an exercise in learning. Screenshot for Microsoft Excel 2010, below:

Microsoft Excel 2010 Screenshot

And below are the Silverlight 3 version screenshots, click here to run the demo app (requires Silverlight 3).

Officer 2010 Screenshot 
Officer 2010 Screenshot

Application Structure

Officer 2010 Solution StructureOne traditional but important type of "separation of concerns" is componentization, and assemblies are often the physical manifestation of such separation. Because the Officer app is rather shallow, there is no great story as far as componentization goes, still I've separated the app into three projects - shell, content and infrastructure related projects. The infrastructure is basically controls and components common to all, the shell is the silverlight "application" that hosts the root visual and styling content, and the content project quite self-descriptively houses the content. 

However the point of having three projects was to highlight the possibility of dependency-mitigation using the Url-based mediator. For example, apart from a single line of code there is no dependency between the shell and the content project. The single line of code (line  18, below), is just used to register the navigation mappings but can equally be rigged in multiple other non-dependent ways. For example, you could dynamically load the content assembly and then ask it register the mappings, or a better approach would be to use something like Prism which offers bootstrapping and module-based initialization entrapments within which you could set the mappings.

The Application Class

The application class as I have mentioned multiple times is conveniently hijacked in nRoute to host a so-called application-wide default container - which by definition handles any navigation without a specified handler. In this case we use the build-in StatefullApplication class, onto which we set the RootVisual and the NaivgationContainer properties as shown below.

public partial class App : nRoute.Navigation.Application.StatefullApplication
{
	private void OnStartup(object sender, StartupEventArgs e) 
	{
		// we create the shell
        var _shell = new Shell();
        this.RootVisual = _shell;

        // we set the default container and set the shell's data context
        this.NavigationContainer = _shell.PresistentContainer;
        _shell.DataContext = this.GetApplicationLinks();

        // we can customize this using deep-linking or something - but for now it is static
        _shell.LinksListBox.SelectedIndex = 1;  
        
        // and we map the assemblies - this is strongly typed, but you could do it many other ways
        ActionService.MapAssemblyActions(this.GetType().Assembly);
        NavigationService.MapAssemblyRoutes(typeof(Officer2010.Content.Views.NewDialog).Assembly);
	}
	// .. other code
}
The code above should be self-explanatory apart from the GetApplicationLinks call and setting index on a list box - which I'll get to below. Note, here we not enabling deep-linking or browser-shell integration, both of which are normally setup at the application class level - though it is perfectly doable.

Navigation Structure

Navigation Structure If you look at the navigation control (shown on the right), it has a set of primary nodes which are both navigable and selectable. However a bit unusual is that it also headlines a couple of links that seemingly are functional and not navigational in nature - like save or open. To represent this structure, I just created two POCO classes, with the primary nodes (Link class type) mapped to navigation Urls and its children (of LinkAction class type) mapped to action Urls. Further, a list of the primary nodes, yielded by in the GetApplicationLinks method in the App class, is set as the DataContext of the root visual (Line 11 above).

The navigation controls is just itself a common ListBox, with a custom ItemTemplate and importantly a customized ItemContainerStyle. And since we know the binding structure, the templates bind to the relevant properties, they also make use of nRoute behaviours attach the navigation and action Urls (see LinksListResources.xaml). Just as a side point, the ItemContainerStyle holds the Lists control's template for the mouse-over and selected visuals, though customizing it is a bit inaccessible in Blend - to customize use the Object > Edit Other Styles > Edit Item Container > . menu hierarchy.

The Shell Structure

The structure of Shell.xaml is really simple, it is basically 3x3 grid with basically a left-anchored list box, a flexible-width container, and a persuado back button up-top. The image below outlines the layout structure:

Click to Open

Custom Containers

With this demo app I wanted to highlight two customization points with Containers, styling and state-management. Because containers by definition model their state related behaviours, I wanted to create a container that could persist its state in Isolated Storage on exit and resurrect it on start - however that was not to be simply because in SL we can't arbitrarily serialize objects, so in a future release I'll do the necessary tinkering with the state management classes to hopefully get this scenario working (however this in entirely possible in WPF). Nonetheless, in the code you'll see how we can still use the isolated storage to persist data, however on a page-by-page basis.

The styling customization was easy pickings; firstly I wanted to put in place some navigation-induced transition effects for which I created a custom control-template that wrapped the presenter in Silverlight Toolkit's transition control, you could also easily use other libraries such as Silverlight FX.

<!-- TRANSITION CONTENT CONTROL WRAPPER -->
<toolkit:TransitioningContentControl RestartTransitionOnContentChange="True">
    <ContentPresenter/>    
</toolkit:TransitioningContentControl>
The other visual tweak I did was to use the SL3 specific child window for highlighting navigation failures, this has much better aesthetics than the MessageBox approach used earlier - try clicking the "Back" button. Lastly, one stupidity I've done with the Containers API is to expose ICommand based properties to trigger navigation, which ensured very clumsily use semantics; so in the custom container I've somewhat correct that by exposing a simple Url property that triggers navigation when changed.

Sub-Containers

One of the common user-cases involve having container like regions within a page, which itself is hosted in a container - that is nothing special really, but with nRoute you get nice-clean and loosely-coupled contraption to play such scenarios out. For example in the NewDialog.xaml page (shown below) we have two lists of document templates (highlighted with yellow diagonal boxes); selecting any item on either of the two ListBoxes triggers a command in the ViewModel, which in turn causes navigation in the sub-container (shown with red stripes) - that then shows the selected document template's details page. The commands are exposed as properties on the ViewModel, and bound to the ListBoxes' SelectionChanged event using nRoute behaviours, and the sub-container is bound to an "ActiveTemplate" property of the ViewModel.

Click for Larger Picture

One of the other useful aspects to sub-containers is that the DataContext of the "host page" flows in the sub-container and any of it's active content, unless specified otherwise. I've used this DataContext inheritance behaviour in OptionsDialog.xaml (shown below); the options page displays Common and Advanced Settings in a sub-container. To hold the settings data I create two POCO classes (the CommonSettings and AdvancedSettings types) and exposed these on the host page's ViewModel as properties. As these two properties "flowed" into the sub-pages, they were used to bind the settings on various UI controls - which really helped, as I didn't have to create extra ViewModels or manage extraneous data exchanges. Also, the POCO classes were earmarked with the DataContract and DataMemeber attributes to enable persistence using the Isolated store in SL, which enables your custom settings to survive multiple disconnected sessions - try it. 

ContainerDataContext

Fishing not the fish

In summary, I hope I've shown the use, design, and customization aspects of Navigation Containers - because they really help in logically breaking-down your application and compose it together as a loosely-couple but cohesive unit. So the real take-away should be how you model your application's visuals using containers as a fundamental building-block - in wiser words the fishing as opposed to the fish itself.

To run the Officer® 2010 app, click here (requires Silverlight 3 runtime).
To get nRoute and Officer® 2010 source code, click here.

PS: The detailed Officer demo app is not a showpiece for architecture or coding practices - it was specifically designed around highlighting certain nRoute concepts. Also, you will be pleased to know no code-behind was harmed in the making of this demo.

Update: Recompiled and updated the source for SL3 RTM version

Comments

trackback
DotNetKicks.com
on 08-May-09 5:54 AM
Trackback from DotNetKicks.com

Step into nRoute

Ivan Shumilin
Ivan Shumilin Russia
on 09-May-09 1:31 PM
There is a bug in App.xaml (Application tag instead of StatefullApplication).

Rishi
Rishi
on 09-May-09 6:45 PM
Actually it works with the application tag, you just need to remove the auto-generated inheritance call, but ya by changing the tag it becomes perfect..

So just for anyone else, change the application class tag in App.xaml to something like this..

<nRoute:StatefullApplication
  ...
  xmlns:nRoute="clr-namespace:nRoute.Navigation.Application;assembly=nRoute.Silverlight">


</nRoute:StatefullApplication>

Thanks Ivan

Robert Bland
Robert Bland United Kingdom
on 11-May-09 2:59 PM
Good job,it certainly looks the part.I think you should turn this into light weight and open sourced Word and Excel replicas.

Rishi
Rishi
on 11-May-09 10:57 PM
@Robert.. Well, I am waiting for Google or some Chinese firm to fund the development of "Office(r) 2010".. LOL.. No, actually I don't need to do anything MS is itself bringing versions of
Office 2010 apps to SL.. maybe they will be ad supported, but they are on their way.. all though, I hope for everyone's eyes sake the text fidelity in SL3 at least matches that of HTML pages..  

mikekidder
mikekidder United States
on 23-May-09 7:29 AM
Rishi, would you prefer general code comments here or in "Discussions" on CodePlex?  Nothing major at all, just some minor code cleanup here and there.  

Just like your Future Desktop demo, this is an excellent demo and shows a lot of the gems available with nRoute.  

One thing I was trying to understand was under "ShareDialogViewModel" was how the RestoreState (in turn calling "LoadState") was being called.  I thought maybe its something implicit with the PersistentContainer, but I am not connecting the dots mentally.

Rishi
Rishi
on 23-May-09 4:23 PM
Hi Mike, thanks and comments are fine here - it's less obscure than codeplex.

Alright, so here is the thing, one of my attempts with this demo was to create a Container that could "automatically" do persistent state management - which is to say that it would store the state in the isolate store on exit and resurrect it on start. However, for that happen every item in the state and the state-collection related classes needed to explicitly opt into the WCF-based serializer mechanism - that was not the case by default. For the next drop I am considering earmarking the classes with the relevant attributes to enable this scenario, but that will create a "supplementary" dependency which is not always needed.

Anyway since I couldn't do that, I wanted to show with the "ShareDialogViewModel" how to directly persistent and load state using the Isolated Store (by using the "IsolatedStore" helper class to save and restore the UserName and Initials values). Also I made use of ISupportNavigationState based call-ins to do that because they provided me with the right "timing" to do the save and restore (the Container calls them when initializing or navigating away from a page). However, I've realized why it might be a bit confusing; actually, before there was a Save and Cancel button on the page, which were hooked up to call PersistState and LoadState methods using ICommads. But looks like, when changing the stuff I did not hook up the Save button to the SaveStateCommand, oops! So as it is now, it only saves when you navigate away and not when you press the save button. I think the OptionsDialogViewModel better shows my intended implementation.

Hope this helps, and thanks for pointing that out for everyone.
Cheers.

Peter
Peter United States
on 03-Jun-09 3:30 AM
it works with the application tag, you just need to remove the auto-generated inheritance call, but ya by changing the tag it becomes perfect..

mikekidder
mikekidder United States
on 02-Jul-09 2:43 AM
In order to get Office(r)2010 demo to work with SL3 RTM, looks like the WrapPanel has moved from "System.Windows.Controls" to "System.Windows.Controls.Toolkit".

Also, update references for:

System.Windows.Controls.Toolkit.dll and
System.Windows.Controls.Layout.Toolkit.dll

located in:

C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Toolkit\Jul09\Bin\

Rishi
Rishi
on 08-Jul-09 3:47 PM
@Mikekidder thanks, I'll update the demo and the source code at codeplex. However my gripe is that this will add quite a lot to the size of xap file.

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading