NavFx Part 4 - Application Structure

Change of plans.  I was going to take you through my journey of creating the next version VRCyclist.com as a way of documenting the ways in which NavFx can be used.  However the amount of work I needed to do just to get the point of having something to demonstrate in this and subsequent articles turned out to be too much so I decided to create new demo applications instead.  I have designed the new demos to be useful as a learning tool as well as demonstrating how you might use NavFx.  To be fair to both camps I have created C# and VB.NET versions of the demo, but I will only be using the C# one in these articles.  I have commented both versions extensively so hopefully this won't be a problem for those whose preference is VB.NET.

So with this article I am going to cover how I envisaged the structure of applications that use NavFx and show how this is applied in the demo.  This is by no means the only way to structure applications or use NavFx but it is the structure that drove the design.  If anyone has a particular scenario in mind that they would like me to cover feel free to post feedback I will do my best to get it covered.

Silverlight Applications

With Silverlight 2.* Microsoft introduced native support for Applications.  On the surface this may just appear a packaging mechanism as an Application file (.xap) is a zip file with a different extension.   However I see it as much more because like a desktop application it always you to package up related components and libraries so that they can be downloaded as discrete packages to run in a browser.  IMHO the Silverlight Application is not dissimilar to the JAVA Applet common on the internet for many years.

For the last few years I have noticed (as I am sure many of you have) a shift to browser based applications, more and more AJAX or similar technologies have been used to get away from postback driven web applications to Rich Interactive Applications(RIA) that run in a browser but feel more like a desktop application.   One problem with these types of applications is that you need a wide range of skills to cover all aspects of development (whether a team or individual) X/HTML, JavaScript, .NET Language (C#, VB.NET etc) and SQL (accepted that LinQ and other technologies may eliminate the need for the latter).   Whilst there are many of us who have all of these skills, it is still a maintenance issue for development shops.  I believe many people are going to see Silverlight 2.* as means of reducing the range of skills required for developing browser applications, whilst increasing the possibilities for rich functionality.  In short I can see Silverlight becoming a primary choice for Line of Business applcations, providing desktop like functionality in a browser.

It is this view point that drove the design of NavFx once I decided to create it, and this viewpoint is re-inforced by the fact that this is exactly what we are doing on my current contract.  We are web enabling a desktop application by creating it entirely with Silverlight 2.* providing the UI and WCF providing the Business and Data services.

Application Structure

When I start out designing an application UI (web or desktop) the first thing I do is define what I like to call a Shell.  The Shell typically provides the controlling and common features of the application.  In a web application this would include things like a Header, Footer, Navigation and any side bars or panels that are going to appear on every page, and as I work with ASP.NET this would involve creating one or more Master Pages.  In a desktop application the Shell would provide the Menubar, Toolbars and a Statusbar.  Once my Shell is in place Icould then concentrate on providing functionality, in a web application this would be through Content Pages or dynamic content created through AJAX, in a desktop application it might be Child Windows, Panels or perhaps an Explorer type application.   I could describe many different scenarios but I think you get the idea.

For a Silverlight Application I will take exactly the same approach.  I will define my Shell then using NavFx dynamically load pages into one or more content panels.  My Shell will contain the navigation elements that control the application and any common features that I want to be always available.  The image below shows the Shell I have created for the NavFx Demos.

NavFx Demo

In this image everything except the centre scrolling pane is part of the Shell for the demo.  The Shell comprises the following parts:

  • Header - top panel
  • Navigation Bar - left side panel
  • Options Bar - top centre panel
  • Watch Bar - righ side panel
  • Output Window - bottom panel
  • Content Panel - centre panel

All sizing is proportional and all elements that are not sized are set to Stretch so that the demo will always fill the browser and layout is dynamically adjusted.  The purpose of the panels are described below.

Navigation Bar

This is a simple StackPanel with Orientation = "Vertical".  It will provide a stack of buttons each loading a different page in the Content Panel designed to demonstrate a feature or usage scenario of NavFx.

Options Bar

This is empty at the moment but will be populated with controls as the demo evolves.  These controls will allow you to control some of the NavFx features.  For example when I get around to demonstrating the Transitions a list of available Transitors will be available in this panel.

Watch Bar

The Watch Bar is designed to give you some insight into what is happening within NavFx at the moment it shows a list of pages Registered with the Navigator instance used by the Shell, a checkbox that indicates whether the selected page is Pinned in the cache and a count of the registered  pages.

Output Window

The grey panel at the bottom displays NavFx specific statements as they are executed.  This is intended to help you learn the code that is required to achieve tasks with NavFx.  I haven't figured out how to get the last TextBlock added to the panel to scroll into view after is is added yet, if anyone knows how to achieve this let me know through feedback and I will update the demos.  The statements in the Output Window are in the appropriate language for the demo and include an indication of the file and method where they can be found in the source code.

How It Works

At this stage the demo comprises two projects NavFxDemo and NavFxDemoPages (NavFxDemoVB and NavFxDemoVBPages for the VB version).  NavFxDemo was created from the NavFxApplication template and NavFxDemoPages was created from the Silverlight Libarary template.

NavFxDemo

The application project comprises four items:

  • App.xaml
  • Shell.xaml
  • Home.xaml
  • Extensions.cs
App.xaml

App.xaml was created from the NavFxApplication template so is an instance of NavFx.Application, which means it is already wired up to NavFx and has an instance of NavFx.Navigator available for use.   As the template was used there is little for me to do (in fact if I weren't providing the Output Window there would be nothing to do at all, as the NavFxApplication template includes a Shell to get you started and it is wired up as the RootVisual ready to go.  However for completenes I will cover what happens within the App.xaml.cs code and how this relates to NavFx.

In App.xaml.cs the App class is defined like this:

public partial class App: NavFx.Application

NavFx.Application derives from System.Windows.Application so App is a Silverlight Application with the addition of a GetNavigator() method added by NavFx.  This method provides access to a private instance of NavFx.Navigator which is instantiated the first time you call GetNavigator().  This provides a psuedo Singleton Navigator for your application but I did not want to force this hence the reason for it being implemented as a "getter" function rather than following a strict Singleton pattern.  In most cases I expect you will use a single Navigator instance but you are not forced to and I will demonstrate in my next article why you might want to use a seperate instance for a page.

The only NavFx code in App.xaml.cs is in the Application_Loaded handler, and to be honest even this does not need to be executed at this point.  The code sets up a default Transitor, but this can be done anywhere as long as it done before the first call to one of the GoToPage overloads, I prefer to set up the default early so my handler contains the following:

this.GetNavigator().Transitor = new SimpleTransitor(App.Current);

So all I am doing is loading my Shell and setting up the SimpleTransitor provided by NavFx as my default page transition handler. Notice I am passing App.Current to the constructor for SimpleTransitor, this sets the Application as the default Target for the Transitor.  NavFx uses the RootVisual property of the Application to work out how to display pages in this scenario.  If the RootVisual implements IHostPage NavFx calls SetContent() on the interface to allow the HostPage to control where new content should be displayed, otherwise it replaces the Children of the RootVisual. There are two other statements in my handler this simply writes the previous NavFx statement to the Output Window using an extension method defined in Extensions.cs and look like this:

Shell.xaml

Shell.xaml is much more interesting than any other item in the demo projects at this time.  I won't waste any time talking about the XAML since apart from the outer element being a navfx:HostPage it is all standard stuff.

Shell.xaml was created from the NavFxHostPage template so is an instance of the NavFx.HostPage, which means it is designed for dynamically loading and displaying pages that either fill the whole page or a specific content area of the page.  As you have seen in previous sections my Shell is designed to display content pages in a panel with other panels surrounding it.  So after defining my UI in the XAML and defining the contentPanel element I need to make sure that this is where content is displayed whenever I navigate to a page.  The IHostPage of NavFx requires that my Shell implement a SetContent(), this will be called by Navigator whenever a call is made to a GoToPage method and the target is either an Application or implements IHostPage.  The HostPage base class provides a default implementation that replaces all children of the layout root for the page with the new content.  This is not the behaviour I want so I need to override the SetContent method like this:

public override void SetContent(System.Windows.Controls.UserControl newContent)
{
      this.contentPanel.Children.Clear();
      this.contentPanel.Children.Add(newContent);
}

The override replaces the contents of the contentPanel with the new content passed by Navigator.

The next thing I want to do with my Shell is have it load Home.xaml into the content panel as the first content page.  I do this in the HostPage_Loaded event handler.  Because Home.xaml is part of the application package the process is quite simple, instantiate the page, register then display it like this:

Home homePage = new Home();
this.Navigator.RegisterPage(homePage);
this.Navigator.GoToPage("Home");

Because the Target property of SimpleTransitor was set to App.Current earlier the GoToPage call will trigger a call to SetContent passing the homePage instance.  Simple as that, I now have my Shell set up to load on startup, and my Home page as the first content page to be displayed.  HostPage_Loaded includes more statements that write output to the OutputWindow but I will leave to investigate these in the source code.  The remainder of the code in Shell.xaml.cs is to provide the functionality of the demo.  Still in HostPage_Loaded there is a call to a helper method to populate the list of registered pages, I will go through this in a moment after I explain this statement:

this.Navigator.PageLoadCompleted += new EventHandler<PageLoadCompletedEventArgs>(Navigator_PageLoadCompleted);

When pages are loaded from external libraries they are loaded asynchronously, and as I worked on the demo I discovered that I needed to know when this completed so I could populate the list of registered pages, so I added a new PageLoadCompleted even to the NavFx.Navigator.  In this statement I am wiring up a handler for this event that will make sure the list is updated after an asynchronous load completes.  The handler simply calls the private helper method loadRegisteredPages(), which contains the following code:

this.registeredPages.Items.Clear();
foreach(IPage page in this.Navigator)
{
    this.registeredPages.Items.Add(page.Path);
}
this.pageCount.Text = this.Navigator.PageCount.ToString();

This uses some more new features I added recently (these do not appear in the quick reference as yet, I will add them soon).  After clearing the list of registered pages it repopulates it using the Enumerator I added recently to support just this kind of functionality, it then updates the count of pages displayed in the Watch Bar.  Strictly speaking these features are not required for Navigation, but I figured if I needed them for something as simple as this demo, you may find them useful in your applications. 

In the Watch Bar there is a checkbox that is read only and is intended to indicate whether the selected item in the registered pages list is Pinned or not.  If a page is Pinned then it will only be cleared from the Page Cache if you explicitly request it.  I will be adding a demonstration of this and writing about it in another article.  In the SelectionChanged handler the registered pages list I have this statement:

this.isPinned.IsChecked = this.Navigator[this.registeredPages.SelectedIndex].IsPinned;

This uses another recently added feature, the Indexer provided by Navigator.  There are two variants of this, the one I am using returns the IPage at a specified Index, the other accepts a string key and returns the IPage with a matching Path.

The final code I am going to cover is that in the handlers for the buttons in the Navigation Bar.  The all follow a similar pattern, set the Target property of the Transitor then navigate to the page using one of the GoToPage overloads provided by Navigator.  All three handlers home_Click, external1_Click and external2_click include this as the first statement:

this.Navigator.GetTransitor<SimpleTransitor>().Target = Application.Current;

Rather than relying on the Target still being the default set during startup I am explicitly setting prior to each navigation call, at this point it is not strictly necessary, but later I plan to add demonstrations that use different Transitors and I don't want to impose any requirements on how the application is used.  This statement uses another new feature added recently the generic GetTransitor method  this returns the current Transitor (if it is set) typed as specified.  It doesn't do any checking so if you specify the wrong type it will throw an exception.  If returns null if the Transitor is not set.

home_Click then calls the most basic GoToPage overload in the knowledge that the page is in the same package and is already registered (this latter assumption will have to change when I add the demonstration for ClearCache:

this.Navigator.GoToPage("Home");

external1_Click calls the overload of GoToPage designed to load and display a page in an external library in one statement:

Uri demoPagesUri = new Uri(@"NavFx.NavFxDemo.Pages.dll", UriKind.Relative);
this.Navigator.GoToPage("PanelPage1",false, demoPagesUri);

This overload of GoToPage expects a Uri instance as the third argument, the Uri must be relative and currently it only works with a dll which must be in the same folder as the .xap file.  These file location and UriKind requirements are imposed by Silverlight not NavFx.  Because of these requirements I added another overload that creates the Uri instance for you and only requires the path, i.e. the dll file name.  This newest overload is the one used by external2_Click:

this.Navigator.GoToPage("PanelPage2",false, @"NavFx.NavFxDemo.Pages.dll");

Well that is all the NavFx specific code in the demos as they stand at the time of writing.  In my next article I will be covering the implementation of a Wizard using NavFx and it's NextPage and PreviousPage methods.  As always feel free to ask questions via the feedback feature here on my blog or the discussions feature on CodePlex.  I am going to wind up with a tip that is not NavFx specific but saves a lot of bother when you are working with Silverlight Libraries.

Keeping Up To Date

When you wire up a Silverlight Application with a web site as I prefer to do, VS very helpfully makes sure the latest version of the .xap file is copied to the ClientBin folder of the web site project (or to the Debug or Release folder if you enable Configuration Specific Folder support), but it doesn't do this for Silverlight Libraries you have to set this up yourself or manually copy the files (forget the latter).  The best way to do this is via the Post Build Event, that way every time you build your library the new build is copied to where it needs to be.  Follow these steps to set up a post build event:

  1. Bring up the properties for the Silverlight Library project
  2. Select the Build Events tab
  3. Enter one of the statements provided below this list into the textbox labelled "Post-build event command line"
  4. Save the project properties

If you are using Configuration Specific folder use this post build command line

xcopy "$(TargetDir)$(TargetName).*" "$(SolutionDir)NavFxDemoWeb\ClientBin\$(ConfigurationName)\" /Y

Otherwise use this one

xcopy "$(TargetDir)$(TargetName).*" "$(SolutionDir)NavFxDemoWeb\ClientBin\" /Y 

 

Previous Article Next Article

 

Print | posted @ Saturday, July 19, 2008 6:45 PM

Comments on this entry:

Gravatar # re: NavFx Part 4 - Application Structure
by Shemesh at 7/21/2008 12:22 PM

hi Mike,
good to see there are developments!

could you plz release a compiled dll(s) of the latest version
  
Gravatar # re: NavFx Part 4 - Application Structure
by Mike Hanson at 7/21/2008 5:42 PM

Yep, doing it now
  
Gravatar # re: NavFx Part 4 - Application Structure
by Shemesh at 7/22/2008 9:33 AM

hi Mike,
tnx for all the work.

regarding the multi-HostPage issue: i would like to suggest a registration mechanism.
same as Pages are held in a dictionary inside Navigator, also HostPage(s) can have a dictionary to hold them.
this would make multi-HostPage pretty easy to use.

what would be even more neat is giving the GoToPage() a HostPage param.
like: ...myNav.GoToPage("KayaPage", "OuterHostPage") ...myNav.GoToPage("KayaPage", "InnerHostPage")

plz tell me if you plan to do it (otherwise i will do it myself in some way)
TNX
  
Gravatar # re: NavFx Part 4 - Application Structure
by Mike Hanson at 7/22/2008 12:45 PM

Hi Shemesh

I understand your suggestion but I am not sure it fits with the extensible design of NavFx. Although Navigator provides the GoToPage methods it doesn't actually do the navigation. The actual transition from one page to the next is the responsibility of your ITransitor implemenation. All the GoToPage methods do after loading a page or retrieving it from the cache is delegate to the TransitionPage method of the current Transitor.

In my demos and applications at the moment I use the SimpleTransitor provided with NavFx and that requires that the Target property be set before making a call to a GoToPage method.

To keep the design extensible and consistent I could make the Target property a part of the ITransitor interface and then provide overloads for GoToPage that accept the Path of a Target page and have it set the Target property of the current Transitor.

This would be a breaking change however so I would have to do something to make a switch for those using NavFx as is painless
  
Gravatar # re: NavFx Part 4 - Application Structure
by Shemesh at 7/24/2008 1:56 PM

Mike,
i get this warning:

The property 'Content' does not exist on the type 'Page' in the XML namespace 'clr-namespace:NavFx;assembly=NavFx'.


any idea?
  
Gravatar # re: NavFx Part 4 - Application Structure
by Mike Hanson at 7/24/2008 9:02 PM

I have tried to resolve that warning but can't find a way. I have put an override for the Content property on the Page class but this has not resolved it.

I think it is something in the Xaml Parser and am hoping it is something that will be fixed in the RTM version of Silverlight.

If anyone finds a way to stop the warning please let me know. Nothing I have tried works.
  
Gravatar # re: NavFx Part 4 - Application Structure
by a.soursos at 7/26/2008 5:34 PM

Hi Mike,
i built a test application on NavFx, but i have a problem, maybe you have a quick answer for me. I've added a service reference in a silverlight control library, which i use with NavFx framework, so i don't have the ServiceReferences.ClientConfig of this library in my main application - xap file, an exception raises that the xapResolver cannot find this file, when i call the service. I googled a little but i didn't find something really useful. Did you faced the same problem? Thanks in advance.
  
Gravatar # re: NavFx Part 4 - Application Structure
by Mike Hanson at 7/26/2008 7:19 PM

You have to have the config file in both applications saddly. I do this by adding it as a link in the Application file, that way any changes you make in the library project will be picked up in the Application.

This is consistent with other aspects of .NET where the config that belongs to the application is the one that is used at run time.
  
Gravatar # re: NavFx Part 4 - Application Structure
by Mike Hanson at 7/26/2008 7:23 PM

If you are likely to have multiple Services you might want to have a Common library project that has all your Service References in and then add a project reference to this library in your application and library projects
  
Gravatar # re: NavFx Part 4 - Application Structure
by Thomas Holloway at 7/28/2008 1:23 AM

In relation to the "Inner Content" parameters. In my original navigation service I added the ability to provide anchors to the link. Such that when you navigate to a page like, Navigator.Go("\some\path\here") you could simply pass in Navigator.Go("some\path\here#anchor"). This way you can pass in as many parameters as you like. If you don't want to do anchors another way is to pass query strings such as: Navigator.Go("some\path\here?query=bleh&innercontent=hi").
  
Gravatar # re: NavFx Part 4 - Application Structure
by Mike Hanson at 7/28/2008 6:33 AM

Thanks Thomas, I will look at adding both mechanisms.

I think however this may not be the Content issue mentioned earlier. When we compile the current NavFx code we get a number of Compiler Warnings about the navfx:Page not have a Content property despite it being derived from UserControl. I have tried providing an explicit Content property on the Page base class, but this doesn't stop the warnings. The warnings appear to be generated when any content is placed between the <navfx:Page ...></navfx:Page> tags in XAML
  
Gravatar # re: NavFx Part 4 - Application Structure
by David Roh at 7/29/2008 12:39 PM

Hi Mike,

I really appreciate your taking the time and effort to share NavFx - also thank you for the less restrictive license.

David Roh
  
Gravatar # re: NavFx Part 4 - Application Structure
by Valentin Stoychev at 8/9/2008 11:34 AM

Great job Mike!
  
Gravatar # re: NavFx Part 4 - Application Structure
by software development london at 8/19/2009 6:29 PM

Interesting,
i get this warning:


The property 'Content' does not exist on the type 'Page' in the XML namespace 'clr-namespace:NavFx;assembly=NavFx'.



Anyway, thanks for the post
  
Gravatar # re: NavFx Part 4 - Application Structure
by MikeHanson at 8/19/2009 8:00 PM

Hmm, that was a problem with the very early releases but I fixed it later. Do you have the latest binaries or code?
  
Gravatar # re: NavFx Part 4 - Application Structure
by Web developer at 10/26/2009 11:55 AM

Cool,

I seem to get this warning alot too,

Thanks for bringing this up
  

Your comment:

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 1 and 7 and type the answer here: