Mouse over one of the steps on the left to see a description.
Commands
Magellan provides a NavigationCommand that can be exposed by your ViewModels and bound to elements on screen. Invoking this navigation command will start a navigation request.
For example:
var command = new NavigateCommand(
"MyController",
"MyAction"
);
button.Command = command;
When clicked, the button will execute this command, which will call the appropriate Navigator and begin the navigation request.
Behaviors
For a better design-time experience with Expression Blend, a behavior can be used. Like the command, when the target control
is clicked, it will locate the appropriate Navigator and begin the navigation request.
Code Navigation
Navigation can also be done programmatically in C#. For global navigation (not pages), it looks like this:
Navigator.Default.Navigate(
"MyController",
"MyAction",
new { customerId = 8 }
);
To navigate within a specified frame, you will need a reference to the Frame or one of the controls loaded within the frame.
Navigator.For(mainFrame).Navigate(
"MyController",
"MyAction",
new { customerId = 8 }
);
The Navigator
The navigator creates the NavigationRequest object and begins the navigation process. It talks to the ControllerFactory
to locate the appropriate controller, and then tells the controller to execute the request. The controller will then be released back to the ControllerFactory
for disposal.
Controller Factory
The Controller Factory is responsible for creating and releasing controllers. It is quite common to implement your own that backs on to an IOC container.
The default controller factory requires manual registration of controllers using lambdas:
var controllers = new ControllerFactory();
controllers.Register("Home", () => new HomeController());
controllers.Register("Mail", () => new MailController());
controllers.Register("Settings", () => new SettingsController());
ControllerBuilder.Current
.SetControllerFactory(controllerFactory);
You will typically write your own controller factory by implementing IControllerFactory and calling the SetControllerFactory method shown above.
This makes it available to the navigator for use.
Controllers
Controllers are the main object you will create using Magellan. A typical controller looks like this:
public class MyController : Controller
{
public ActionResult MyAction(int customerId)
{
Model = LoadCustomer(customerId);
return View();
}
}
The default Controller class implements IController. When executing the request it
defers to an IActionInvoker to find the method, call the action filters, and handle the
result.
Before your controllers can be used, they must be registered with the controller factory.
The Action Invoker
The action invoker is the workhorse for the controller, and handles most of the request execution. The default implementation takes care of:
To control this process yourself, you can implement the IActionInvoker interface and assign your action invoker to your controller before the request is processed (typically by customizing the controller factory.
Pre-Action Filters
Pre-action filters are attributes that implement the IActionFilter interface. The action invoker calls
all pre-action filters before the action is called. Action filters can set an OverrideResult property to cancel the navigation or take an alternative path - for example, prompting the user
for credentials or redirecting to a different action.
Action filters are typically applied using attributes:
[RoleFilter("Admin")]
public ActionResult DeleteRecord()
{
}
Their implementation is quite simple:
public class RoleFilterAttribute : Attribute, IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
if (IsInRole(RoleName))
return;
context.OverrideResult = new CancelResult();
}
}
Actions
Actions are typically the methods implemented on the controller. Parameters can be passed to the
action as method arguments, and model binders are used for mapping
the parameter values. All actions must be public and must return
ActionResult objects.
public ActionResult ShowCustomer(int customerId)
{
Model = LoadCustomer(customerId);
return View("Customer");
}
Post-Action Filters
Post-action filters are attributes that implement the IActionFilter interface. The action invoker calls
all post-action filters after the action has been called, whether it succeeds or fails. Post-action filters get access to the action result and can be used to make modifications to it,
and they get access to any exception that was thrown which they can use to replace or suppress it.
View Results
Actions can return any object that derives from the ActionResult base class.
The most common result is ViewResult, though others exist such as CancelResult
and RedirectResult. Action results are in charge of realizing the final output of the navigation request.
Window View Engine
The Window view engine attempts to find a Window or dialog with the right name, and shows it to the user. It requires the view to be
inherit from the WPF Window base class. To choose between showing the view as a regular Window versus showing it as a dialog, you can implement the
IWindowView interface:
public class MyWindow : Window, IWindowView
{
public void ShowView()
{
// Other code can go here
ShowDialog();
}
}
Page View Engine
The Page view engine attempts to find a Page with the right name, and shows it to the user. It requires the view to be
inherit from the WPF Page base class. The Navigator that was used needs to have access to an INavigationService
in which the page will be shown.
Region View Engine
The Region view engine makes use of Composite WPF to show a view inside a region. The view must derive from the
Control base class or higher. To specify the region, a controller would typically use:
return View("MyView").InRegion("TopLeftRegion");
Where TopLeftRegion is a region registered in the default Composite WPF RegionManager.
Discussion
Daniel Vaughan
This is great work Paul! The demo is very slick indeed.
Cheers, Daniel
Carl Scarlett
Magellan looks fantastic Paul. Saves me from writing it myself!
What's the licensing model for Magellan? And moving forward, what plans do you have for maintaining and extending it?
Cheers, Carl.
Paul Stovell
Hi Carl, licensing will be open source under the New BSD license - I'll add that tonight to make it clear. Maintenance wise, I have a plan, just need to get someone to agree to it :)
Ryan Riley
Despite my curiosity as to why we need yet another framework, I do really like this MVC approach, and I think you've done a terrific job (from a cursory scan). I suppose part of it is my jealousy that you had time to work on this very same think I wanted to tackle. Seems to happen a lot. I'm looking forward to trying this out.
(On a side note, I noticed you are using Markdown in an ASP.NET MVC app. What library are you using. I would like to use this, but the only library I found was quite old and didn't seem to have great reviews.)
Cheers! Ryan
Paul Stovell
Hi Ryan, I'm using the Markdown.NET library from Brian Jeremy. You can check out the source code to my blog to see what I do with it. For comments I also use this input sanitizer over the results to prevent XSS.
Miguel
Paul,
I saw your TODO list for Magellan and I noticed one of your points is to port it to Silverlight. In recent projects I've been using Prism and the Navigation framework and I'm not happy with the results. In an attempt to do something better I slowly started doing something way simpler than Magellan, but similar in principle. It's working fine for our current scenario, but I'll consider something like Magellan for other projects. How are you doing with the port? Would you need any help?
Paul Stovell
Hi Miguel, I'd love some. I'll shoot you an email.
Gerhard
Hi Paul,
I just read about the PreAction filter. So how is the whole story, can I gray out a button, if his action needs some priviledge I don't have? I talk about WPF scenario. You know, there is a Command object which expose an Execute method and a CanExecute function.
With best regards
Gerhard