A picture of me

Welcome, my name is Paul Stovell. I live in Brisbane and work on Octopus Deploy, an automated deployment tool.

Prior to founding Octopus Deploy, I worked for an investment bank in London building WPF applications, and before that I worked for Readify, an Australian .NET consulting firm. I also worked on a number of open source projects and was an active user group presenter. I was a Microsoft MVP for WPF from 2006 to 2013.

Back to: Magellan Home

When building WPF pages and windows, it's common to want a consistent layout across views. In ASP.NET, this is often accomplished with Master Pages. Magellan brings a similar concept to WPF, in the form of Shared Layouts.

The Magellan source code includes a new Wizard example application that demonstrates the shared layout feature. In this page I'll describe how it works.

The new Wizard sample

Creating and Using Shared Layouts

To create a shared layout, I typically add a Shared folder under Views:

A Visual Studio Project with a Shared folder containing Main.xaml, a shared layout, and a number of pages which use the layout

The shared layout, Main.xaml, is simply a UserControl with a number of ZonePlaceHolders. Each ZonePlaceHolder is given a Name, which we'll refer to later. The example below shows how a three-column layout might be declared. Since the layout is a UserControl, it can contain any XAML you wish:

        <ZonePlaceHolder Name="Left" DockPanel.Dock="Left" Width="300" />
        <ZonePlaceHolder Name="Right" DockPanel.Dock="Right" Width="300" />
        <ZonePlaceHolder Name="Content" />

Now that our layout is declared, we can reference it from any other Window, Page or UserControl. Here's what a page might look like:

    Title="Account Details"
    <Layout Source="/Wizard;component/Views/Shared/Main.xaml">
        <Zone ZonePlaceHolderName="Content">
            <TextBlock Text="The center content goes here" />

        <Zone ZonePlaceHolderName="Left">
            <TextBlock Text="This content will appear on the left" />

        <Zone ZonePlaceHolderName="Right">
            <TextBlock Text="This content will appear on the right" />

The page uses the Layout control to reference the shared layout. The Source property is the URI to the the XAML file that contains the shared layout.


The Layout element is a control that takes a Source and a set of Zones. When it is loaded into the scene, it loads the shared layout user control and sets it as the content. Any ZonePlaceHolders inside the control will then have their content injected, based on the zone names matching.

The logical tree of the final page will look like this:

A diagram showing the logical tree of the page, layout and zones

Since the Layout is a child of the Page, this opens a number of possibilities:

  • The layout will inherit the DataContext of the page. If your DataContexts for each page have the same properties in common - such as using a shared View Model - the layout can make use of those.
  • The Layout can use WPF's Routed Events to communicate with the page.
  • The layout can use RelativeSource FindAncestor bindings to get properties from the page, such as the page title.

Default Zone Content

The ZonePlaceHolders are content controls, and their content is overridden when the layout is merged into the page, but only if the page specifies a corresponding zone. This means you can set default content for zones, and allow individual pages to override them.

For example, wizards often have a Back button. Using WPF Pages, the ZonePlaceHolder could be written as:

<ZonePlaceHolder Grid.Column="0" Name="BackNavigation">
    <Button Content="Back" Command="NavigationCommands.BrowseBack" />  

If a page that refers to the layout doesn't specify a Zone with the same name, the back button will appear. But a page could override the content, for example:

<Zone ZonePlaceHolderName="BackNavigation"> 
    <Button Content="Cancel" Command="{Binding CancelWizardCommand}" />

Or it may just choose to clear the content, removing the button from the tree:

<Zone ZonePlaceHolderName="BackNavigation" Content="{x:Null}" />

Default Shared Layouts

Since the Layout control's Source property is a dependency property, you can use a Style to set the default layout source:

<Style TargetType="Layout">
    <Setter Property="Source" Value="/Wizard;component/Views/Shared/Main.xaml" />

When writing pages, you can now just use a Layout element without a source:

    <Zone ZonePlaceHolderName="Content">
        <TextBlock Text="The center content goes here" />

Or a page may choose to override the Layout:

<Layout Source="/Wizard;component/Views/Shared/Alternative.xaml">
    <Zone ZonePlaceHolderName="Content">
        <TextBlock Text="The center content goes here" />

Interestingly, thanks to dependency properties, you could also use a Trigger or data binding to selectively change the layout based on user preferences.

Designer Support

Shared Layouts have good designer support with Expression Blend. For example, this is how the Main.xaml page from the Wizard example appears:

The Main.xaml file in Expression Blend

The individual pages that reference the shared layout also work in Blend:

A page in Blend that uses the shared layout

The only caveat to make it work in blend is that the URI must contain the assembly name of the layout. For example, this will work at runtime, but not design time:

<Layout Source="/Views/Shared/Main.xaml" />

However this will work at both design time and runtime:

<Layout Source="/Wizard;component/Views/Shared/Main.xaml" />

Nested Layouts

Nested layouts allow you to apply "inheritance" to layouts. For example, you can set up a common layout that provides a Title and Content zone. You can then create another layout that references the first layout, and sub-divides the content into two column, Left and Right. You might create another layout subdividing the two column layout even further - it's layouts all the way down.

A nested layouts example

Nested layouts are easy to create. First the Main.xaml:

        <ZonePlaceHolder Name="Title" DockPanel.Dock="Top" />
        <ZonePlaceHolder Name="Content" />

Now the TwoColumn.xaml, which references Main.xaml:


    <Layout Source="/MyAssembly;component/Layouts/Main.xaml">        
        <Zone ZonePlaceHolderName="Title">
            <ZonePlaceHolder Name="Title" />

        <Zone ZonePlaceHolderName="Content">
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />

                <ZonePlaceHolder Grid.Column="0" Name="Left" />
                <ZonePlaceHolder Grid.Column="1" Name="Right" />

Note how this layout references the first. To re-expose the Title zone, we create a Zone with a ZonePlaceHolder. We also expose a Left and Right zone by splitting the Content zone using a Grid.


Shared Layouts allow you create a consistent look and feel for your views while minimizing XAML and code behind. They can be used not only on Pages, but from any XAML - you might create a Shared Layout for Dialogs with OK/Cancel buttons, or for tab pages within an options dialog.

To make use of shared layouts, you just need a reference to Magellan.dll. You don't have to use Magellan's MVC framework to use this feature, as they are completely independent.

I'd love any feedback on how to make this feature more useful.

Back to: Magellan Home

Back to: Magellan Home

Magellan makes use of TraceSources internally for providing diagnostic information. By default nothing is traced, but you can configure the trace sources to see details.

Configuring the trace sources can be done through the application configuration file:

<?xml version="1.0" encoding="utf-8" ?>
      <source name="Magellan" switchValue="Verbose">
          <clear />
          <add name="Console" type="System.Diagnostics.ConsoleTraceListener" />

Or through code, for example, in the App.xaml code behind:

Magellan.Diagnostics.TraceSources.MagellanSource.Switch.Level = SourceLevels.Verbose;

Here is an example of what is logged at the Information level:

Magellan Information: 0 : The Navigator is executing the request 'Controller=Home; Action=Home; RequestID=6ce8e373-f2d9-48d0-b1c2-9438094feeaf'.
Magellan Information: 0 : Controller 'iPhone.Applications.Home.Controllers.HomeController' is executing request 'Controller=Home; Action=Home; RequestID=6ce8e373-f2d9-48d0-b1c2-9438094feeaf'.
Magellan Information: 0 : The PageViewEngine is rendering the page 'iPhone.Applications.Home.Views.HomeView'.
Magellan Information: 0 : Request completed: 'Controller=Home; Action=Home; RequestID=6ce8e373-f2d9-48d0-b1c2-9438094feeaf'.

Here is what is logged at the Verbose level:

Magellan Information: 0 : The Navigator is executing the request 'Controller=Home; Action=Home; RequestID=2704ddc6-0dca-412f-88fc-b08a008ee586'.
Magellan Verbose: 0 : Resolving controller 'Home' for request 'Controller=Home; Action=Home; RequestID=2704ddc6-0dca-412f-88fc-b08a008ee586'
Magellan Information: 0 : Controller 'iPhone.Applications.Home.Controllers.HomeController' is executing request 'Controller=Home; Action=Home; RequestID=2704ddc6-0dca-412f-88fc-b08a008ee586'.
Magellan Verbose: 0 : DefaultActionInvoker found the action 'Home' as method 'Magellan.Framework.ActionResult Home()'
Magellan Verbose: 0 : DefaultActionInvoker found the following action filters for action 'Home': ''.
Magellan Verbose: 0 : DefaultActionInvoker found the following result filters for action 'Home': ''.
Magellan Verbose: 0 : The ViewEngineCollection is consulting the view engine 'Magellan.Framework.PageViewEngine' for the view 'Home'.
Magellan Information: 0 : The PageViewEngine is rendering the page 'iPhone.Applications.Home.Views.HomeView'.
Magellan Verbose: 0 : The PageViewEngine has navigated to the page 'iPhone.Applications.Home.Views.HomeView'.
Magellan Verbose: 0 : The PageViewEngine is clearing the navigation history.
Magellan Information: 0 : Request completed: 'Controller=Home; Action=Home; RequestID=2704ddc6-0dca-412f-88fc-b08a008ee586'.

The Warning and Error levels are rarely used - warning is used when Magellan decides on a course of action that is potentially incorrect, and errors are usually logged before an exception is thrown.

See also:

Back to: Magellan Home

Back to: Magellan Home

Magellan 1.1 brings support for asynchronous controllers. First we'll look at what it enables, then I will show some ways to configure it.

The controller action below makes a WCF call to load a list of customers, which are used as the model for the view. This service call could take some time to evaluate:

public class CustomerController : Controller
    public ActionResult List()
        Model = CustomerService.GetCustomers();
        return Page("CustomerList");

With asynchronous controllers enabled, Magellan will invoke the List action on a background thread. Since page navigation has to take place on the UI thread, the page rendering will be automatically dispatched to the UI thread, but this happens after the controller action has been executed. The result is that the UI remains snappy and responsive.

Enabling Asynchronous Controllers

There are three simple options for enabling asynchronous controllers. The first option is to inherit from AsyncController instead of Controller:

public class CustomerController : AsyncController

The second option is to assign the AsyncActionInvoker when the controller is constructed (this is actually what both AsyncControllerFactory and AsyncController do):

public class CustomerController : Controller
    public CustomerController() 
        ActionInvoker = new AsyncActionInvoker();

The preferred approach is to rely on controller factories to set the action invoker. Instead of using CoontrollerFactory, you can use AsyncControllerFactory:

var controllerFactory = new AsyncControllerFactory();
controllerFactory.Register("Home", () => new HomeController());
controllerFactory.Register("Customer", () => new CustomerController());

If you are using a custom controller factory, you just need to replace the ActionInvoker - for example:

public ControllerFactoryResult CreateController(NavigationRequest request, string controllerName)
    var controller = // Create controller
    if (controller is ControllerBase)
        ((ControllerBase) controller).ActionInvoker = new AsyncActionInvoker();
    return new ControllerFactoryResult(controller);

Reporting Progress

Now that navigation is occurring on a background thread, it's nice to show a progress indicator while navigation is happening. For this, Magellan now provides an INavigationProgressListener interface that you can implement.

public interface INavigationProgressListener
    void UpdateProgress(NavigationRequest request, NavigationStage navigationStage);

For example, the code behind for the Window is going to show a progress bar:

public partial class MainWindow : Window, INavigationProgressListener
    public MainWindow()
        Loaded += (x, y) => Navigator.For(Frame).NavigateWithTransition("Home", "Home", "ZoomIn");

    public void UpdateProgress(NavigationRequest request, NavigationStage navigationStage)
            new Action(delegate
                switch (navigationStage)
                    case NavigationStage.BeginRequest:
                        ProgressBar.Visibility = Visibility.Visible;
                    case NavigationStage.Complete:
                        ProgressBar.Visibility = Visibility.Collapsed;

The iPhone sample application uses a spinning circle to indicate navigation progress:

A spinning indicator is used

The NavigationStage enum provides an indication as to where the request is up to. The table below explains each state:

1 BeginRequestThis occurs at the start of the navigation request before the controller has been resolved.
2 ResolvingControllerThis indicates that the controller is about to be resolved by name from the current controller factory.
3 ResolvingActionThis indicates that the controller has been resolved, and the action is now about to be resolved.
4 PreActionFiltersThis indicates that the action has been resolved, and pre-action filters are about to be invoked.
5 ExecutingActionThis indicates that pre-action filters have been invoked, and the action is about to to executed. This event does not always occur (pre-action filters can cancel the navigation request, for example).
6 PostActionFiltersThis indicates that the action has been invoked, and post-action filters are about to to executed. This event does not always occur (pre-action filters can cancel the navigation request, for example).
7 PreResultFiltersThis indicates that the action has been executed and all action filters have been invoked. The result is now about to be evaluated. This event does not always occur (action filters can cancel the navigation request, for example).
8 ExecutingResultThis indicates that the pre-result filters have been executed, and the result is about to be executed (this is typically when views are rendered). This event does not always occur (action filters or pre-action filters can cancel the navigation request, for example).
9 PostResultFiltersThis indicates that the result has been executed (and views have been rendered) and the post-result filters are about to be executed.
10 CompleteThis indicates that the navigation request has been completed (whether successfully or failed), any views have been rendered and any resources from the navigation request have been cleared up.


The benefit of this approach is that controller code is the same whether we are using single threads or background threads, which also allows unit tests to remain singly threaded while at runtime the application is multi-threaded. Enabling this feature is quite easy, and so long as your controllers don't depend on the UI thread it should Just Work.

Back to: Magellan Home

(This page is a work in progress)

High Level Architecture

  • User Journeys
  • Deployment
  • UI approaches
  • Navigation Model
  • Inversion of Control
  • Managing third-party dependencies
  • Solution structure

Separated Presentation

  • Model View Presenter/Supervising Controller
  • Model View ViewModel/Presentation Model
  • Model View Controller
  • Pub/sub Eventing
  • Observer pattern


  • Top down composition
  • Bottom up composition
  • View-based composition
  • Model-based composition
  • Modular applications
  • Plug Ins

Solutions to Common Problems

  • Threading
  • Validation
  • Editing and IsDirty
  • Contextual awareness
  • Shared Layouts/Master Pages
  • Disposability
  • Unit testing
  • Automated UI testing
  • Authentication/authorization
  • Enforcing UI standards
  • Forms
  • Performance and memory management
  • Online/offline/limited access mode
  • Enabling automation/macros
  • Theming
  • Help

Enterprise Applications

  • Centralized configuration
  • Localization
  • Designer support

Client Server Applications

  • Proxy management
  • Routing
  • Messaging
  • Server events/duplex bindings
  • Client vs. Server side domain models
  • Sharing code between Client and Server
  • Sharing validation between Client and Server

Development processes

  • View specifications
  • Designer/developer interaction
  • Migration

Cross Cutting Concerns

  • Logging
  • Exception management
  • Caching


  • Coding standards

Back to: Magellan Home

Magellan does not natively support Windows Forms, but adding support is as easy as writing your own View Engine.

As described in the documentation on View Engines, there are two classes we need to write. The first is an object that implements IViewEngine, which is responsible for finding the view. The second is an object inheriting from ViewEngineResult, which contains logic for rendering the form (i.e., calling Form.Show()).

There are also a couple of design considerations:

  • Magellan controllers like to create View Models, and it's the ViewEngineResult's job to make them available for binding on the view.
  • Windows Forms applications typically use naming conventions like frmCustomerDetails, CustomerDetailsForm or CustomerDetailsWindow. Our engine should support these.

Binding Models to Forms

To support the model, we're going to create an interface that allows the model to be given to the form. The form can then figure out how to display the model (either using a Windows Forms BindingContext, or manual code).

public interface IBindableForm
    void Bind(object model);

Our form implementation could look like this:

public partial class MainForm : Form, IBindableForm
    public MainForm()

    public void Bind(object model)
        Text = model.ToString();

The Controller

Here is what our Controller might look like:

public class HomeController : Controller
    public ActionResult Launch()
        Model = "Hello world!";
        return Window("Main");

Notice that there is no reference to Windows Forms here. The Window() return value merely indicates that we want to show a Window - it doesn't indicate what kind of Window should be shown. This should make it easier to convert to WPF in the future.

The View Engine

The view engine is also pretty short. Since we want to reuse the naming conventions and reflection code that the other view engines use, we can derive from ReflectionBasedViewEngine:

public class FormsViewEngine : ReflectionBasedViewEngine
    private readonly IViewActivator _viewActivator;

    public FormsViewEngine(IViewActivator viewActivator)
        _viewActivator = viewActivator;

    protected override IEnumerable<string> GetAlternativeNames(string viewName)
        yield return viewName;
        yield return viewName + "Form";
        yield return viewName + "Window";
        yield return viewName + "Dialog";
        yield return "frm" + viewName;

    protected override bool ShouldHandle(ControllerContext controllerContext, ParameterValueDictionary viewParameters, string viewName)
        var viewType = viewParameters.GetOrDefault<string>("ViewType");
        return viewType == "Window" || viewType == "Dialog";

    protected override IEnumerable<Type> FilterCandidateTypes(ControllerContext controllerContext, ParameterValueDictionary viewParameters, string viewName, IEnumerable<Type> candidates)
        return candidates.Where(x => typeof (Form).IsAssignableFrom(x));

    protected override ViewEngineResult CreateViewResult(ControllerContext controllerContext, ParameterValueDictionary viewParameters, Type type)
        return new WindowsFormViewEngineResult(type, _viewActivator, viewParameters);

Notice how we override GetAlternativeNames to support our conventions. We also override FilterCandidateTypes to restrict the view to objects derived from the Windows Forms Form class.

The View Engine Result

The View Engine above has taken charge of finding the view. When a view is found, it creates a WindowsFormViewEngineResult. The view engine result has the job of instantiating and rendering the view:

public class WindowsFormViewEngineResult : ViewEngineResult
    private readonly Type _formType;
    private readonly IViewActivator _viewActivator;
    private readonly ParameterValueDictionary _viewParameters;

    public WindowsFormViewEngineResult(Type formType, IViewActivator viewActivator, ParameterValueDictionary viewParameters) : base(true, new string[] {})
        _formType = formType;
        _viewActivator = viewActivator;
        _viewParameters = viewParameters;

    public override void Render()
        var form = (Form)_viewActivator.Instantiate(_formType);
        var bindableForm = form as IBindableForm;
        if (bindableForm != null)
            var model = _viewParameters.GetOrDefault<object>("Model");

        var isDialog = _viewParameters.GetOrDefault<string>("ViewType") == "Dialog";
        if (isDialog) form.ShowDialog();
        else form.Show();

We make use of two critical view parameters - the ViewType, which indicates whether to show it as a Window or a Dialog, and the Model, which is populated from the Model property that was set on the controller.

Wiring It Up

To support our view engine, we just have to add it to the list of registered view engines:

ViewEngines.Engines.Add(new FormsViewEngine(new DefaultViewActivator()));

The DefaultViewActivator implements the IViewActivator interface, which is used for actually constructing the form (it's passed to the view engine result). If you want, you can use an IOC container instead.

The final Windows Form


Hopefully this illustrates how easy it is to write a View Engine. The biggest benefit to the Magellan approach is that our controllers are consistent whether we are using WPF or Windows Forms. There's also the added benefit that if we upgrade a view to WPF, our controllers don't change. Likewise if we change our naming conventions for the forms, we don't need to change the controllers. Finally, our controllers and models remain testable, which is always important.

Back to: Magellan Home

(This page is a work in progress)

As the primary Microsoft platform for building client applications, Windows Presentation Foundation has a rich validation system. This article is going to look at the approaches that WPF enables for validation, how to style validation messages to suit your needs, as well as some of the limitations of the system. This article is targeted at .NET 3.5 SP1.

How to Validate

WPF allows you to declare validation logic in the following places:

  • You can declare a rule in XAML and apply it to a binding (UI only)
  • You can throw exceptions in property setters (models)
  • You can implement the IDataErrorInfo interface (models)

Validation Rules via XAML

WPF allows your rules to be encapsulated within a class, and to have the rule applied via XAML. You begin by defining a rule:

Code sample goes here

Your rule can then be applied by using object element syntax to declare your binding:

XAML code goes here

This will be enough to have error messages appear on screen:

Screenshot goes here

We will talk more about what you can do with validation rules later.

Throwing Exceptions in Setters

An easy way to perform validation in models is to throw an exception in the setters of your properties:

Code sample goes here

When you declare a binding, you can either use object element syntax, as per below:

XAML syntax

Or make use of a handy property that was introduced in .NET 3.5:

XAML syntax (shorter)

This approach is useful because it allows you to consolidate validation logic in your models, but it has a few drawbacks which we'll discuss later. A better approach to model validation is the IDataErrorInfo interface.

Implementing IDataErrorInfo

WPF in .NET 3.5 introduced IDataErrorInfo support out of the box. The implementation is quite simple:

Code sample goes here

As with model binding, you can either use object element syntax:

XAML sample here

Or a handy attribute that was introduced in .NET 3.5:

XAML sample (shorter)

How to display validation results

WPF makes it easy to display validation errors against a UI element.

Back to: Magellan Home

Because the Magellan framework handles many parts of the navigation lifecycle, there are a number of things that can go wrong. I want to dedicate this page to explaining the common exceptions that are thrown by Magellan, and look at techniques for handling them.

Exceptions thrown by Magellan

Where logical, Magellan throws standard .NET exceptions such as ArgumentNullException and InvalidOperationException. However, Magellan also has it's own set of custom exceptions that can be thrown. All custom Magellan exceptions derive from an abstract NavigationException class, which you can use if for some reason you want to handle all Magellan exceptions in a specific way (such as from a common exception policy handler).


This is usually thrown if there is something in Magellan that has not been setup properly. This can be thrown when:

  • You forget to set the controller factory via ControllerBuilder.Current.SetControllerFactory when the application starts up. See the section on controllers and controller factories for more details.
  • There are no View Engines registered, because they have been cleared and none were added. By default the View Engines are automatically registered by Magellan, so the application must have code somewhere that clears the view engines for this to happen. See the topic on View Engines for more details.


This is thrown when a navigation request has been made, but there is no logical way that Magellan could handle it. An example that causes this is when you use Navigator.Default to navigate to an action, and the action attempts to navigate to a page. Magellan can't navigate to the Page without knowing the NavigationService, so there is no way to fulfill this request. See the section on the Navigator for more details.


This exception is thrown when an action is not found on the controller. This usually indicates that the controller exists, but the name of the action is incorrect, or the wrong controller was specified. Generally, actions should be public, non-static/shared methods, and must be declared as returning ActionResult or a derived type.


This is thrown when an action threw an exception, and it wasn't handled by any of the Action Filters. Some pseudo code for how actions are invoked is below:

   call action()
catch ex
   ask any of the action filters to handle it
   if handled = true, do nothing
   else, throw UnhandledActionInvocationException(ex)

As you can probably guess, the reason for throwing a custom exception instead of re-throwing the original, is that Magellan would lose the stack trace, which would make tracking the real exception down very hard. So instead, Magellan wraps it in this exception. You can check the InnerException property to see the real cause for the exception.

As the code above suggests, Action Filters get a chance to handle exceptions. I'll talk more about that below.


This guy is thrown when the action was executed fine, but when the ActionResult which it returned threw an exception. This can happen for example if you return a StartProcessResult, but the process name doesn't exist.

As with action invocation, the action filters will get a chance to see and handle this exception too. If they choose not to, this exception will be thrown, with the real exception nested safely inside the InnerException.


This hopefully self-explanatory exception is thrown when you return a Page, Window or Dialog action result, but the view name you specify doesn't exist. This exception also includes the names of all of the attempted search locations where Magellan looked for the view in the aptly-named SearchLocations property.

Techniques for Handling Exceptions

As with any exception handling guidance, the best approach is to avoid exceptions from happening in the first place. However, that's not always possible.

To avoid UnhandledActionInvocationException and UnhandledActionResultException, you can make use of Action Filters. The action filters section has more details.

Here is an example action filter that logs all navigation errors and shows a message box when they occur:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HandleErrorsAttribute : Attribute, IActionFilter, IResultFilter
    public void OnActionExecuting(ActionExecutingContext context)

    public void OnActionExecuted(ActionExecutedContext context)
        if (context.Exception != null)
            context.ExceptionHandled = true;

    public void OnResultExecuting(ResultExecutingContext context)

    public void OnResultExecuted(ResultExecutedContext context)
        if (context.Exception != null)
            context.ExceptionHandled = true;

This can then be applied to a controller as simply as:

public class MyController : Controller ...

Your filter can also override the exception - for example:

public void OnResultExecuted(ResultExecutedContext context)
    if (context.Exception is SqlException)
        context.Exception = new GenericException("A database issue occurred.");

That said, for the most part if your intention is to just log and suppress all exceptions, you can do so via the Application.Current.DispatcherUnhandledException event without involving Magellan. There are occasions where this form of handling could come in handy however.

Back to: Magellan Home

Back to: Magellan Home

When navigating between pages, transitional animation can provide a powerful means of communicating context with the user. The Transitionals library from Microsoft is a popular way to set up transitions when content changes, and comes with a number of out of the box transitions such as 3D cube, blinds, dissolve and star wipe.

When it comes to page navigation, we often want transitions to relate to the navigation we are performing. For example, if we click a "Next" button, we might expect a transition that 'slides' the next page into view. If we then clicked "Back", we would expect a slide in the opposite direction.

Magellan has out of the box support for the Microsoft Transitionals library. Here I will assume that you have a Magellan project ready (you can create one manually using this Quickstart, or just use the Magellan project template).

You will need to add a reference to the following DLL's:

  • Magellan.Transitionals.dll
  • Transitionals.dll

You will then need to update the entry point of your application (usually App.xaml.cs) to set up the transition mappings:

NavigationTransitions.Table.Add("Back", "Forward", () => new SlideTransition(SlideDirection.Back));
NavigationTransitions.Table.Add("Forward", "Back", () => new SlideTransition(SlideDirection.Forward));
NavigationTransitions.Table.Add("ZoomIn", "ZoomOut", () => new ZoomInTransition());
NavigationTransitions.Table.Add("ZoomOut", "ZoomIn", () => new ZoomOutTransition());

These entries define a transition, as well as the 'reverse' transition that will be used when navigating "Back".

To enable the transitions, you can edit the control template of any Frame elements to use the transitional navigation element. Below is an example:

<Frame Name="mainFrame">
        <ControlTemplate TargetType="{x:Type Frame}">
            <DockPanel ClipToBounds="True">
                <NavigationTransitionPresenter Content="{TemplateBinding Content}" />

Finally, you will need to change any code which performs a navigation request to specify the transition to use. Procedurally, code like this:

Navigator.For(mainFrame).Navigate("Home", "Index");

Should become:

Navigator.For(mainFrame).NavigateWithTransition("Home", "Index", "ZoomIn");

And likewise, Behaviors like this:

<Button Content="Try Again">
        <NavigateBehavior Controller="Home" Action="Index" />

Should become:

<Button Content="Try Again">
        <NavigateWithTransitionBehavior Transition="Forward" Controller="Home" Action="Index" />

And that is all! Navigating between pages should now make use of transitions.

When defining your transitions using the TransitionTable, you can provide any transition from the WPF Transitionals library, or one of the four out of the box Magellan transitions designed specifically for navigation applications (slide forward, slide backward, zoom in and zoom out). For example, below is a roll transition in action:

Roll transition

NavigationTransitions.Table.Add("Forward", "Back", () => new Transitionals.Transitions.RollTransition());

Writing your own transitions is also relatively easy - see the Transitionals source code for examples.

Back to: Magellan Home

I was getting bored with dark colour schemes in Visual Studio, so about a month ago I put together a colour scheme that matches PaulStovell.com. The colours my blog uses are inspired by StackOverflow, with a few small differences to make it fit with the other colours I use.

Here is how C# code looks:

C# code

Here is how XAML looks:

XAML code

And XML files:

XML code

Download the colour scheme (for Visual Studio 2008).

You can also get it from StudioStyles:


I thought it would be interesting to list out some of the technologies that run PaulStovell.com. I wonder how similar my setup is to others and any recommendations people have on improving it.


The infrastructure that runs PaulStovell.com is housed in a massive dedicated data center in Brisbane. And by massive data center, I actually mean my apartment. Next to the TV in my living room is a black desktop that used to be a media center, until I installed Windows Server 2008 on it and started using it as a server. It's a quad core AMD Phenom, 64-bit, with 6 GB RAM. The machine is still connected to the TV via HDMI - if I need to change anything, I use my remote control to flip to HDMI 2 and use a wireless keyboard to log into the machine. I also tend to notice when the machine is offline since the room becomes a little quieter :)

The other hardware that makes it all happen is a wireless ADSL model from iiNet, and a 1 TB external hard disk that I use for backup. The internet comes through an iiNet business plan (business 3), which gives me upload speeds as fast as download speeds and a static IP address. I tend to get fast response times in Australia but I'm not sure what the experience is like overseas.

Source Control

All of my source code is kept in Subversion, at svn.paulstovell.com. It's actually the Visual SVN server, running on a local IP address of Since I only have one external IP address, I have IIS 7 acting as a reverse proxy to the Visual SVN server (which is really just Apache) based on host name.


When I check code into source control, it's built automatically using JetBrains TeamCity, at build.paulstovell.com. Unfortunately since some of my builds make use of TeamCity's parameter system for passing information like passwords to builds, I can't make the build server public (I'd love some tips on parameterizing the builds safely so I can open it up).

The TeamCity build dashboard

It might seem overkill to have a build server at home, but one of the niceties is that I can check in a change for Magellan or PaulPad (below), and the build server can build it, run the tests and deploy it for me - everything is automated, which means I'm more likely to make small changes.


The site that you are looking at is a custom built Wiki+Blog engine that I wrote in a couple of weeks (after 6 months procrastinating) called PaulPad. The source code to PaulPad is also under Subversion. PaulPad was written ASP.NET MVC using NHibernate and a splash of jQuery. The content is composed using Markdown, and I use the reverse engineered WMD editor for composing the posts. I use Prettify for code highlighting because it allows me to keep the markup perfectly clean and the highlighting is done automatically. Comments are sent to Akismet for spam checking.

One of my favourite PaulPad features, apart from the clean markup, is that I can selectively publish page edits to an RSS feed when editing a page. For example, if I publish something, and correct a mistake a few minutes later, it will appear in the current feed. But in 6 months time, if I overhaul the page, I can "re-publish" it back to the feed so it appears in RSS readers again. That makes the blog+wiki concept really work for me.

File Server

When my TeamCity build server produces builds, I wanted to make them available via a page that simply showed files off the file system. However, the default IIS file list pages look a little ugly for me. So I created FileServer, which is the engine behind get.paulstovell.com.


I use the SmarterStats package for web statistics. It's installed at stats.paulstovell.com, but there doesn't seem to be a way to make it public without giving everyone a login. I love that it's written in ASP.NET and the graphs use Silverlight. It also just looks nice. It's a shame my only visitor is my Mum :)

My SmarterStats dashboard for the last 7 days

To make the statistics more accurate, I send a 1x1px tracking image with all of the RSS feed entries that I serve up. The images all point to "/(page-name)/via-feed", which allows me to see pretty accurately how many people read my content via feeds as opposed to viewing the pages directly. I'd like to know if anyone thinks this is dishonest.


It's not that I don't trust myself, but, I have a habbit of monkeying around with my server, which impacts the general uptime. I try to keep it to a limit, and I use Pingdom.com to give me statistics about my uptime. Pingdom can also SMS me when the site is down, but given that the computer is in my lounge, I usually know when it's down, and if I'm not home I'm probably unlikely to fix it.

You can see the Pingdom graphs online: www.pingdom.com/reports/8vrndjlwezen/.


This is about the only thing I don't host myself. I use Google Apps to handle paulstovell.com mail.


Given how half-baked this whole setup is, I figure I should at least have a good backup system. I use MozyPro to upload all content, database backups, and Subversion stores as soon as changes are made and also during a nightly backup process. I also use MozyPro on my laptop.

I also use a 1 TB external hard drive, and a scheduled task in Windows to robocopy a set of folders to the drive. It's really a "just in case" should the internet go down while I'm out of town, and it's also just nice to have more backups.

For the database I've setup a maintenance task that backs up all databases every four hours and ships them to a folder to be backed up by Mozy. I recently had to restore a backup from Mozy for the PaulStovell.com database after, well, I screwed up :) The backup worked well, so it's nice to know I can rely on it. I should probably make a point to test my backups more often.


In the past I have used shared web hosting, virtual machine hosting and dedicated hosting with a few different providers, but I always missed the kind of control I wanted. Even with dedicated hosting, the thought of having to remote desktop into the servers usually put me off, not to mention the costs. Hosting it all in house was very worthwhile, and given my Pingdom uptime of 97% I'm pretty happy with the reliability.

Something I'm very proud of is that there isn't a single line of PHP or Perl on my setup - it's all .NET (with a little Apache and Java). In fact PaulPad's best feature is that it isn't Wordpress :)

The Subversion and Team City combination works really well together, and because everything is on the LAN it is very fast. I notice my workflow at home is very different to my workflow at work. Here's my build log from TeamCity when I was working on Magellan:

TeamCity build log

What other services should I try? What are some alternatives that you prefer?