次の方法で共有


Silverlight Navigation Part 3

Welcome to the third post in my series on navigation in Silverlight! The goal of this series of posts is to provide a simple to use framework (called Helix) for building Silverlight applications that are based on a flexible navigation mechanism and the ViewModel separated presentation pattern. This post is long and was a tough one to write (which is partly why it took so long to complete!) but this post takes us much closer to our goal.

If you’ve been reading this series from the beginning, you’ll have noticed that we’re taking something of an agile approach to building Helix. You can think of each post in this series as an iteration. And like all good agile projects, we’re not afraid of a bit of refactoring, or of adding or significantly altering features as we tackle new scenarios. So in the latest version of Helix you’ll find some changes compared the version in part 2.

Some of these changes are bug fixes, but most are new features or extensions to existing features to support the ViewModel pattern alongside navigation. You’ll also find more samples and unit tests in this drop. There is a summary of the main changes and new features at the end of this post. You can download the latest release from here and you can run all of the latest samples from here.

The Story So Far…

Before we get into any details, let’s quickly review what we’ve covered in previous posts…

In the first post in the series, I introduced the Helix Frame and NavigationLink controls and showed how they could be used together with a simple Journal to implement a simple navigation mechanism between pages in a Silverlight 2.0 application.

In the second post in the series, I introduced the Route, NavigationHandler and NavigationController classes and demonstrated how they can be used to implement navigation between pages based on parameterized navigation links. That post also showed two ways of passing parameters via the navigation link to the target page, so that the page can be initialized or have it's context set appropriately.

One way was to use the NavigationPageController class which passes all of the navigation parameters to the target page's OnNavigate method (through the INavigationPage interface). The other way was to use the PageActionController class which invokes a page action method on the target page by mapping the navigation parameters to the method's name and it's parameters.

Both of these approaches created an instance of the target page and then passed the navigation parameters directly to it. As noted in the second post, this is clearly not ideal since it means that the page will then contain application logic as well as UI – not good for a lot of reasons!!!

Separated presentation patterns like the ViewModel pattern help to keep the UI and application logic cleanly separated. I described the ViewModel pattern in a previous post, but to recap, the ViewModel pattern separates UI and presentation logic into View and ViewModel classes, and uses data binding and commands to communicate between them. In this post, I provided an implementation of a generic ViewModel for handling collections, imaginatively called CollectionViewModel. In this post, I described two strategies in which the View and the ViewModel can get created and linked up. In View-First composition, the view is logically created first. In Presenter-First composition, the presenter (the ViewModel is our case) is logically created first.

These three additional posts can be considered parts 2a, 2b and 2c of this series, so please check them out if you haven't already done so. We’ll be building on the concepts and features they describe in this post.

Ok, that’s the background information out of the way. Let’s get started extending the Helix navigation framework so that it works alongside the ViewModel separated presentation pattern…

The Challenge: Mixing ViewModels and Navigation

One of the challenges with any separated presentation pattern is that you have to have some way of creating, hooking up and managing the additional components that are involved. In our preferred ViewModel pattern these additional components are principally Views, ViewModels and Models.

Now, if your application is fairly static, it’s fairly straightforward to create and hook up these components. But if your application is more dynamic (for example, when the UI changes constantly because the user is navigating around within the application!) then things are not quite so simple. As well as creating and hooking up these components during navigation, we also have to figure out how the other parts of the application are going to interact and communicate with them.

In my experience, this issue – the handling of Views, ViewModels and Models in a dynamic application that includes navigation – is the one that causes the most confusion and pain for folks building real-world applications using the ViewModel pattern. It is this very issue that we’re going to tackle in this post…

Choosing A Navigation Scheme

Before we get into the details on the integration of the ViewModel pattern into the Helix navigation framework, we first have to think about the different ways in which we can structure our application so we can and navigate around within it.

Helix essentially allows you to design a navigation scheme for your application by allowing you to decide how the various elements in your application are linked together by parameterized Uri's. When you define the format of the navigation links in your application, you are designing the way in which the user will navigate through your application to access its functionality and content by identifying the main navigational elements in your application and identifying which parameters are passed to them. At a high level, there are two basic navigation schemes to choose from.

Perhaps the simplest navigation scheme is one where the application's navigational links refer directly to the application's visual elements. Using this scheme means that we'll link the application together using Uri's that refer to pages or controls within the application and allow the user to navigate between them. This is the navigation scheme that we used in the last two posts of the series. I think this type of navigation scheme tends to work great for content-centric applications – i.e. when the user expects to be primarily navigating through content. Blog readers, news readers, and media sharing apps are good examples of this type of application.

With Helix you are not limited to using simple page-based navigation schemes. You can link your application together in other interesting ways. In particular, you can structure your application so that it's composed of tasks or actions that the user carries out. I call these types of applications task-centric applications.

Let's look at a simple example – an expense reporting application. In terms of tasks, the user can create, edit, submit, approve, reject, or archive expense reports, so we could structure our application so that the user naturally navigates between these tasks in sequences that are natural. For example, the user may start by creating a new expense report and then submit it for approval; then they may edit a draft expense report and save it for later submission; then they may review their approved expense reports and archive them away for safe keeping.

I think this type of navigation scheme is very interesting. It means for instance, that we can align our application’s navigational structure with the application's use-cases and the user’s tasks, rather than having to strictly follow the visual structure of the application. This means that we can more cleanly separate the visual structure of the application from its logical structure and work flow, which in turn means that we can more easily test the application, and have more flexibility when designing a fluid, animated UI for it. In addition, when we consider deep linking (in a future post) we'll see that the user, or other systems, very often will be exposed to the navigational structure of the application, so having a navigation scheme that’s independent of the visual structure can also be important for many other reasons.

At this point you might be asking: “Yes, but does it really matter what the navigational scheme for an application is since the user never sees it – they just click on links and the UI gets updated?”. This is of course true, up to a point. It’s not clear that one approach is superior to the other – I can see advantages and disadvantages with each – so we’ll explore both approaches to see what each feels like when building an application.

Whatever your choice of navigation scheme, content-centric or task-centric, the important thing is that you are comfortable with it and use it consistently throughout your application. Using multiple schemes within the same application can lead to user confusion and an application that’s difficult to maintain or extend. Choosing a navigational scheme that's natural for your application will help you build, test and extend it much more easily and will help to make it easier to use, so it’s important to design your navigation scheme wisely and to stick to it.

Ah, But What About The ViewModel?

Regardless of the navigation scheme, things get even more interesting when we introduce the ViewModels pattern into the navigational picture. Using the ViewModel pattern means that we'll have a component that encapsulates the UI (the View), and a component that encapsulates the presentation logic and state (the ViewModel). My post here describes the ViewModel pattern in some detail.

The View and the ViewModel have to get created and hooked up together and this can happen in one of two ways – the view is either logically created first followed by the ViewModel on which is depends, or the ViewModel is logically created first followed by the View on which it depends. I call these two mechanisms View-First or ViewModel-First composition respectively – see my post here for more details.

Now – and this is a key point – if you think about a content-centric navigation scheme as described above, you'll see that this maps very naturally to View-First composition! The user is logically navigating to a View so we construct the View first, followed by the ViewModel on which it depends and that provides a clean separation between UI and presentation logic.

On the other hand, with a task-centric navigation scheme, the user is logically navigating to a component that manages an activity or task. This component (as we'll see below) maps quite naturally to a ViewModel, so logically we're creating the ViewModel first, followed by the View on which it depends so that it can be visually represented in the UI. In other words, task-centric navigation maps naturally to ViewModel-First composition!

So, we can clearly identify two distinct navigational styles:

  • View-First Navigation – Where the user navigates between Views in a content-centric application,
  • ViewModel-First Navigation – Where the user navigates between ViewModels that represent tasks or activities within the application.

Interestingly, you can also see the correlation between content-centric navigation and view-first composition, and task-centric navigation and presenter-first composition, in ASP.NET web applications.

With traditional ASP.NET web applications, the application consists of a number of pages that are linked together by simple Uri's. In other words, the navigational structure of the application is based on visual elements linked together. With ASP.NET WebForms, the pages can be richly defined using server-side controls, client-side AJAX controls, and with MasterPages and so on, but the navigational structure is always based on pages and the ASP.NET runtime will create the pages as you navigate to them. You may optionally choose to separate the presentation or business logic from the UI by using an MVP pattern, but in this case, the Presenter will be constructed by the page itself.

On the other hand, using ASP.NET's MVC framework, we can define a navigational scheme that follows the logical structure of our application and that is independent of the visual pages within the application. By defining suitable navigation routes we will logically navigate to a controller component which then allows us to create a suitable view and hook it up to a data model component. We technically have controller-first composition, but hopefully you can see that this is the moral equivalent of constructing a ViewModel before the View.

In this latest drop of Helix, I’ve added support for both View-First navigation and ViewModel-First navigation by implementing a navigation controller for each. The rest of the Helix architecture remains essentially the same (though there are some new features and some fixes as detailed at the end of this post). Remember, you can always implement a custom controller if you want to change the implementation that’s described below.

Instead of walking through the implementation of these controllers, it’s probably more useful to walk through a sample application for each navigation style so you can see how they are both put together. In the examples below, I’m also using some of the new and improved features in Helix that are common to both styles of navigation.

Ok, enough theory, let’s see some code!

View-First Navigation

To illustrate View-First Navigation, I took the Order Entry sample application from the second post and converted it to use ViewModel pattern. You’ll remember that this sample was based on a navigation scheme where the links referred to the pages within the application, so it was relatively straightforward to keep that navigation scheme and introduce the ViewModel pattern into it. You can run the final application here. Let’s walk through the application so you can see how it all fits together…

Helix1The application consists of a number of Models, ViewModels and Views, plus a RootPage and an Application  class. The RootPage is a simple page with a Frame control – as we navigate between pages, they will displayed here.

The application uses three model classes to represent the customer, sales order and the sales order line items. In this sample these are just simple classes with a number of properties. These are all defined in the Models folder.

The ViewModels for the application are defined in the ViewModels folder. The ViewModels implement a number of commands and properties that the Views can bind to. The application’s pages are all defined in the Views folder. None of the pages have any code behind; all of the UI is defined declaratively in XAML. The pages bind against the ViewModel’s commands and properties.

All of these classes are connected together at run-time through navigation. The navigation scheme for the application is defined in App.xaml. With the latest drop of Helix, we can define the navigation routes, and associated controller and default parameter values, as an application resource. This allows us to define the navigation scheme for our application declaratively without having to write any code.

 <n:RouteTable x:Key="GlobalNavigationRoutes">
     <n:Route UriTemplate="/[PageName]">
         <n:Route.Controller>
             <c:PageController DefaultResourcePath="Views" />
         </n:Route.Controller>
     </n:Route>
     <n:Route UriTemplate="/[PageName]/[CustomerID]">
         <n:Route.Controller>
             <c:PageController DefaultResourcePath="Views" />
         </n:Route.Controller>
     </n:Route>
     <n:Route UriTemplate="/[PageName]/[CustomerID]/[SalesOrderID]">
         <n:Route.Controller>
             <c:PageController DefaultResourcePath="Views" />
         </n:Route.Controller>
     </n:Route>
 </n:RouteTable>

In this application we have three navigation routes. The first route defines no parameters just the page name, but the second and third routes add CustomerID and SalesOrderID parameters. Each route uses a PageController. The DefaultResourcePath tells the controller where to look in the application for the pages that we will navigate to.

The bulk of the application’s logic is defined in the ViewModel classes. The ViewModels implement a number of commands and properties and expose them to the UI for binding. Helix includes a number of features to help with View to ViewModel binding.

Simple properties exposed by the ViewModel are easy to bind to. In the case where the underlying data managed by the ViewModel is a collection (for example, a collection of customers), the ViewModel creates a CollectionViewModel class to wrap it. This class manages currency, selection and sorting of the underlying collection. The View and CollectionViewModel are kept in sync using the SynchronizeCollectionView attached behavior like this:

 <ListBox ItemsSource="{Binding Customers}"
      h:SelectionChanged.SynchronizeCollectionView="{Binding Customers}">

My post here on CollectionViewModel describes in detail how this attached behavior works. I have now rolled the CollectionViewModel and the SynchronizeCollectionView attached behavior classes into Helix so everything is in one place.

The ViewModels also implement commands that the View can bind to. Commands are implemented using the Prism DelegateCommand class – see here for more information on Prism and Delegate Command pattern. Commands can be bound to UI elements using the Prism Command attached behavior. Command parameters can also be defined using the CommandParameter attached behavior.

 <Button Content="Edit"
     prism:Click.Command="{Binding ViewOrdersCommand}"
     prism:Click.CommandParameter="{Binding Customers.CurrentItem}" />

In the example above, we’re binding the Edit button to the ViewOrders command and passing in the currently selected customer as a command parameter. When the user clicks the button, the command’s method will be invoked with the current customer as a parameter. If the Edit command is not available, the button is disabled automatically.

Many of the commands in the application result in a navigation. Commands which result in navigation are typically implemented like this:

 private void ViewOrders( Customer customer )
 {
     // Navigate to the Orders page.
     Uri navUri = new Uri( string.Format( "/Orders/{0}", customer.CustomerID ),
                           UriKind.Relative );
     _navigationContext.Target.RequestNavigate( navUri );
 }

The Uri above is formatted in accordance with the navigation scheme that we defined in App.xaml. The Uri can include parameters such as the customer ID and these are passed to the target ViewModel when it’s created. This makes it very easy to pass parameters around during navigation. Since we have a View-First navigation application, we could also use the Helix NavigationLink control and navigate directly from the page.

The navigation context you can see above is passed to the ViewModel by the framework when it is first constructed as a result of navigation. You can use the navigation context class to get access to the navigation parameters, or to ask the navigation target to navigate to another Uri as shown above. The NavigationContext class is discussed in more detail below.

There are just a couple of final things that we need in order to stitch everything together. Since we’re using a View-First navigation controller, navigation Uris, such as the one above, specify the name of the page to navigate to. In the example above, we’re navigating to the Orders.xaml page in the Views folder. So far so good, but how does the framework know which ViewModel to create for this view?

There are a number of ways to solve this problem. Ultimately we need to know the Type of the ViewModel class so we can instantiate it. One way to get this type is to infer it from the type of the View – so, for example, a view with a type name of ‘MyApp.MyView’ would infer a ViewModel of type say ‘MyApp.MyViewModel’. This would work but means that we would have to know about the naming convention and the relevant namespaces and we would have to follow both carefully when creating our ViewModel classes.

Another approach, and the one that is implemented in this sample application, is to allow the View to explicitly specify the type of the ViewModel it was designed for. To do that we just use a simple attached property. The Customers page, for example, specifies that it needs an instance of the CustomersViewModel class like this:

 <NavigationPage
  x:Class="Microsoft.Samples.Helix.OrderEntrySample.CustomersPage"
  ...
  ViewModel.TypeName=
    "Microsoft.Samples.Helix.OrderEntrySample.ViewModels.CustomersViewModel">

You can imagine some day in the far flung future, when the tooling folks finally embrace the notion of separated presentation patterns, that the ViewModel’s type could be automatically specified by the tool when the user is designing the view – maybe even as a proper Type parameter making the View a generic type, for example MyView<ViewModelType>. We can only hope that that day will come soon…

In any case, when the framework creates the View it checks to see if there is a ViewModel specified, and if so, it instantiates the ViewModel and sets it as the View’s DataContext. All of this is handled by the PageController which is described in more detail below.

The last piece of the puzzle is the navigation context. Once the View and ViewModel have been created and hooked up, the controller passes in the navigation context which contains the navigation parameters and a whole bunch of other stuff. The ViewModel (or the View) can opt to receive the navigation context by implementing the INavigationAware interface, which consists of a single method OnNavigate:

 #region INavigationAware Members
  
 public void OnNavigate( NavigationContext context )
 {
     // Store the navigation context for later.
     _navigationContext = context;
     ...
 }
  
 #endregion

The ViewModel can then retrieve the parameters it’s interested and retrieve the relevant data to initialize itself.

The PageController Class Dissected

The View-First application described above uses the PageController class to handle navigation. This controller does all of the heavy lifting and stitches everything together. It carries out four steps in sequence:

Helix2

  1. The first step is to create the View. The View’s name is specified as a parameter as part of the navigation Uri, as defined in the navigation routes in App.xaml. The View is created by loading the resource, prefixed with the DefaultResourcePath if one is specified.
  2. The next step is to create the ViewModel. The ViewModel's type is specified using an attached property value on the View. If the View doesn’t specify a ViewModel type, this step is skipped.
  3. If a ViewModel has been created, the next step is to set the View’s DataContext to the it. This allows the View to binding to the ViewModels properties and commands.
  4. The final step is to set the navigation context on the View or ViewModel.

The last step probably needs some explanation. In part 2 of this series you may recall that we passed the navigation parameters to the target View using the INavigationPage interface. Now that we have ViewModel support, we probably don’t want to pass the navigation parameters to the page; we want to pass them to the ViewModel instead. In the latest drop of Helix, I have added a new interface INavigationAware that can be implemented by a View or a ViewModel (or both). The PageController checks to see if either the View or the ViewModel implements this interface and if so, calls the OnNavigate method, passing in the NavigationContext.

NavigationContext is a new class. It provides a single object from which to retrieve all of the information about the current navigation operation, including the navigation parameters, mode, Uri and target, etc. Most of the ViewModels in the sample application store a reference to the NavigationContext to use for initiating further navigation operations later on and for retrieving the parameters that they require.

The implementation of the PageController class described here is pretty simple. The strategy it implements is the one I settled on during my investigations, but you can easily change the implementation to suit your needs. For example, you can change the way in which the View or ViewModel gets created, or they way in which they get hooked-up, to whatever strategy you prefer. The beauty of the Helix framework is that you can make these changes in a single place without having to make multiple changes throughout the entire application!

ViewModel-First Navigation

OK, so much for View-First navigation. Let’s see what ViewModel-First navigation looks like…

To illustrate ViewModel-First navigation, I implemented a sample expense reporting application. You can run the application here. As described above, this application provides a number of tasks that the user can carry out. Each task will be rendered visually, so the user will experience these states through the UI, but to a large extent the tasks exist independently of how they are rendered.

The following diagram illustrates the tasks and the commands that lead to the transitions (navigation!) between the tasks. From the View Expense Reports task, for example, the user can create, edit, review, submit or archive an expense report; from the Edit Expense Report task the user can save the expense report and the application moves back to the View Expense Reports task.

Helix5

Helix3The structure of the project is similar to the Order Entry sample described above. It consists of three folders that contain the application’s Models, ViewModels and Views, a root page and an application class.

The essential difference between this application and the Order Entry application described above is that navigation occurs between ViewModels. Each ViewModel represents a task so the bulk of the application logic is defined in the ViewModels. Each ViewModel implements properties and commands similar to those described above.

The ViewModel folder also contains a couple of ViewModel helper classes that perform data formatting for expense amounts and dates. These classes aren’t ViewModels per se, but they are used by the Views to help them render the data correctly.

There is a corresponding View for each ViewModel, defined in the Views folder. Again, the views contain no code behind.

Ok, let’s see how all of this fits together. First, the application’s navigation scheme is defined in App.xaml:

 <RouteTable x:Key="GlobalNavigationRoutes">
     <Route UriTemplate="/[ViewModelTypeName]/[ExpenseID]">
         <Route.Controller>
             <ViewModelController DefaultResourcePath="Views"
                 DefaultNamespace="Microsoft.Samples.Helix.ExpenseSample.ViewModels"/>
         </Route.Controller>
         <Route.Defaults>
             <NavigationParameter ParameterName="ExpenseID" ParameterValue="-1" />
         </Route.Defaults>
     </Route>
 </RouteTable>

This application defines a single route that has two parameters – the ViewModel type name, and the expense report ID. A default value for the expense report ID is specified, so the navigational Uri’s don’t necessarily have to specify it. This route uses the ViewModelController navigation controller class. This is the class that coordinates how the ViewModels & Views get created, hooked-up, and initialized with the navigation context.

The ViewModelController has two properties – DefaultResourcePath, which tells it where to look for Views, and DefaultNamespace, which specifies the default namespace for the ViewModel classes. The operation of this controller class is described below, but essentially it works by creating a ViewModel then a suitable View for it, hooks them up via the View’s DataContext and then sets the navigation context.

The interaction between the Views and the ViewModels is the same as described above and includes commands and the CollectionViewModel sync attached behavior. Navigation between tasks occurs as commands are invoked on the ViewModels. A typical implementation of a command looks like this:

 private void CreateExpenseReport( ExpenseReport expenseReport )
 {
     ExpenseReport newExpenseReport = _expenseReports.CreateNewExpenseReport();
  
     // Navigate to the Edit ViewModel.
     _navigationContext.Target.RequestNavigate( new Uri(
             string.Format( "/EditExpenseReport/{0}", newExpenseReport.ExpenseID ),
                            UriKind.Relative ) );
 }

The format of the Uri is as defined in the navigation route in App.xaml. In the example above, we’re passing an expense report ID as a parameter to the target EditExpenseReport ViewModel. When that ViewModel is instantiated, the parameter will be passed in as part of the navigation context and the ViewModel can retrieve the corresponding expense report. In this way, we can control not only what gets instantiated during navigation, but also how parameters flow around the system. This really simplifies how applications like this are put together…

The ViewModelController Class Dissected

Hopefully you won’t be surprised to learn that the ViewModelController creates the ViewModel first and then creates a suitable View for it. The sequence goes like this:

Helix4

  1. In the first step, the ViewModel is instantiated using the ViewModelTypeName navigation parameter, prefixed with the DefaultNamespace if one is specified. So for a ViewModel type name of ‘EditExpenseReport’, the type ‘Microsoft.Samples.Helix.ExpenseSample.ViewModels.EditExpenseReport’ will be instantiated.
  2. The next step is to create a suitable View. The details of this step are described below. If a View can’t be instantiated, an exception is thrown.
  3. The next step is to set the View’s DataContext to the ViewModel. This allows the View to binding to the ViewModels properties and commands.
  4. The final step is to set the navigation context on the View or ViewModel.

In the View-First strategy described above we had the challenge of figuring out which ViewModel to instantiate for a given View. In ViewModel-First navigation we have the opposite problem – given a ViewModel, we have to decide which View to instantiate for it.

The current implementation takes a simple approach – we just append the ViewModel’s short type name with ‘View’ and load the corresponding View resource. So for the ‘EditExpenseReport’ ViewModel, we’d load the View ‘EditExpenseReportView’ located in the Views folder. This was an experiment to see how a naming-convention based approach would work.

There are other ways to solve this problem, but for the purposes of this sample, it works pretty well. You can always implement an alternate strategy in a custom controller class if you prefer a different way to create and hook-up the ViewModel or View.

End of Part 3

Well, that was a long post wasn’t it? We covered a lot of ground though. Since we’re building Helix from the ground up there are a lot of details to grok, and just like sausages and laws, it’s not always pretty seeing how things are made. The final framework, though, should be drop-dead simple to use – that’s the goal anyway!

We’ve been experimenting with alternate approaches to discover their advantages and disadvantages and to see which one might work as a default approach that will work for most applications. We’ve essentially identified two broad approaches that have potential and we can now build applications with a View-First navigational style, or with a ViewModel-First navigational style.

Which approach you use depends on your application and which approach feels most natural to you. The implementations described here work pretty well for both approaches but there are few things that need some improvement. The nice thing about Helix, though, is that it’s simple to implement alternate strategies or to tweak the behavior of the navigation scheme in an application. With a few changes here and there we can customize navigation without having to rewrite big chunks of our application.

Personally, I believe the ViewModel-First approach has a lot of advantages over View-First approach, but it takes some getting used to. The example above doesn’t really highlight the advantages of this approach in terms of testability and UI flexibility. I’ll try and focus on those in the next post in the series, as well as deep linking. I promise that you won’t have to wait as long for that post…

On another note, whatever approach we choose, there is always the ‘tooling problem’. In other words, what would the ideal tooling experience be like for building applications based on both a separated presentation pattern and a flexible navigation system? It’s not an easy question to answer. I have some ideas but they will have to wait for a future post too…

As always, please let me know what you think of Helix – what you like or don’t like, etc. If you have any feedback on Helix, please drop me a line.

oO------Oo

Helix 0.3: Changes and New Features

A quick summary of the main changes and new features in this drop of Helix:

  • Added support for global routes. Global routes are managed by the Navigation Manager and can be defined as an application-level resource in XAML. If not global routes are defined, a default global rout is added automatically. Frame-local routes can also be defined and are checked for a match first during navigation. If a match at the Frame level is not found, then the global routes are checked. This provides a lot of flexibility when implementing hierarchical navigation scenarios in an application.
  • Moved the page loader code to the static Navigation Manager class. Centralizing this code allows for multiple controller classes to take advantage of it (and allows for it’s ultimate replacement by the Silverlight framework itself if and when it supports the ability to load pages by resource name).
  • Removed the need to register the application’s main assembly with the Navigation Manager class. The Navigation Manager now searches all assemblies in the application manifest (except those beginning with System). This also the Navigation Manager to instantiate Views and ViewModels without the developer having to specify which assembly they are defined in, which in turn allows for easier integration between UI and non-UI application layering.
  • Default constructors added for Route and Journal classes. Using the default constructor for a Route object instantiates a default PageController instance. Similarly, using the Journal’s default constructor instantiates a default RouteNavigationHandler instance. These default constructors allow for simpler declarative setup in code or XAML.
  • NavigationParameter and related classes were also changed to support default construction and the ability to declaratively specify default values in Routes.
  • Refactored the regular expressions in the Route class to const strings to allow for easier customization. Also tidied up the regular expression for identifying fields in the navigation Uris. This allows you to more easily change which characters are allowed in navigation fields (e.g. you can now define fields that include period and dash, so you can use GUIDS for example).
  • Refactored all Controller and Journal classes into their own namespaces.
  • NavigationUIVisibility is now in the Helix Controls namespace.
  • Controller parameters – added the ability for a controller to specify its required parameters using an attribute. This allows the controller base class to automatically check incoming parameters to make sure that all required parameters have been provided. This simplifies error handling and the implementation of derived controller classes. This also allows for the associated Route object to check for a compatible controller when the Route is first registered.
  • Navigation Context – Added Navigation Context class which includes all of the information pertaining to the current navigation operation, including the navigation parameters, original Uri and the navigation target. This provides for a simple API, better extensibility and allows ViewModels to store a reference to their navigation context so they can initiate or initiate/cancel/redirect a navigation operation.
  • Added PageController and ViewModelController classes. Removed the NavigationPageController class (this functionality is now provided by the PageController class). The PageActionController class is now derived from the PageController class.
  • Added DefaultResourcePath property on PageController & ViewModelController to allow views to be loaded from a sub-folder in the project. The previous version of Helix required all pages to be defined at the project root level.
  • Added InitialUri dependency property on the Frame Control. This allows the Frame to navigate to the specified Uri when the Frame is first loaded, removing the need for code behind in the root page.
  • Added CollectionViewModel to Helix library. Also added a number of bug fixes in the Refresh and OnModelChanged method. The latter is required due to weird behavior with the Silverlight DataGrid control.
  • Added a unit test project with unit test for Routes, Navigation Manager, Page and ViewModel Controllers, CollectionViewModel, etc.
  • That’s all the changes I can remember…

A quick note on Silverlight 3.0 Beta: Helix and all of the associated samples are built for Silverlight 2.0. However, you should have no problem using Helix with the Silverlight 3.0 Beta. There are a few known issues in Silverlight 3.0 Beta to do with memory leaks but these probably won’t affect you too much and they’ve been fixed in later versions.

Comments

  • Anonymous
    April 28, 2009
    Please, go on. This information is priceless.

  • Anonymous
    April 29, 2009
    The comment has been removed

  • Anonymous
    April 29, 2009
    Thanks for the feedback mhnyborg. Yes, I could certainly have used the Dependency Injection pattern and Unity to hook-up the View and ViewModel. There are a couple of things with that model I don't like but it's certainly do-able. I'll put together a post on that and explore the options. I'm already using some Prism features in Helix - namely the DelegateCommand stuff - so they coexist side-by-side pretty nicely. There are some Prism features like modularity and regions that can really tie into Helix more deeply. I'm working on a blog post to show that too.

  • Anonymous
    April 29, 2009
    Thank you for submitting this cool story - Trackback from DotNetShoutout

  • Anonymous
    April 29, 2009
    Thank you for submitting this cool story - Trackback from progg.ru

  • Anonymous
    April 30, 2009
    Thanks for your work on Helix and the CollectionViewModel. Pretty awesome ! I implemented the CollectionViewModel code for a datagrid. I noticed I get an error when I select a row in the grid and then sort on any column by clicking on the header. The count of the CollectionViewModel object seems to become zero. I fixed this simply by putting in a check condition for the count. There may be some better solutions I'm not aware of which could fix the same issue. Regards,

  • Anonymous
    May 01, 2009
    @winrunner - Thanks for your feedback, and thanks for calling the DataGrid issue out. I'll take a look at that and see if I can see what's going on. I've noticed that the DataGrid behaves differently when bound to an ICollectionView than other controls. I thought I'd accounted for these differences but it looks like I need to take a more careful look...

  • Anonymous
    May 03, 2009
    Hi David. Are there some ways to integrate your Navigation framework with Navigation framework that comes from Silverlight 3.0 beta 1? Could you please to write some thoughts about it? Sorry for English.

  • Anonymous
    May 04, 2009
    I am interested in your thoughts on Erchan's thoughts, as well as your reason for creating your own route handler. Was the ASP.NET MVC route handler not sufficient or not a workable solution?

  • Anonymous
    May 04, 2009
    @Erchan - Yes, I'm working on a post to talk about navigation in SL3 & Helix. There are some similarities between SL3 navigation and Helix, but there are limitations with SL3 due to the lack of extensibility points... I'll keep you posted. @riles01 - I would have loved to have used the ASP.NET Routing engine. Unfortunately, there are a couple of things that prevented me from doing so. First, it's a full .NET Framework assembly so won't work in Silverlight. Second, it's really designed for ASP.NET so it has some dependencies on specific web classes, like HttpContext, etc.

  • Anonymous
    May 18, 2009
    In my last post – Silverlight Navigation Part 3 – I outlined a mechanism whereby you could navigate to