Magellan Introduction
Back to: Magellan Home
Magellan is a lightweight framework that makes it easy to build WPF navigation applications. It is inspired by the ASP.NET MVC framework. The main features are:
- Model-View-Controller support
- Action filters for cross-cutting concerns such as authorization and redirection
- Blend behaviors to make navigation easy
- Transitions between pages
Magellan was drawn from a number of samples I had put together early this year and some work done on a client project.
The source download includes an "iPhone" application for demonstrating the features.
We start with a simple project structure:
A controller implementation typically looks like this:
public class PhoneController : Controller
{
public ActionResult Group(Group group)
{
var contacts = _contactRepository.GetContacts(group);
Model = new GroupViewModel(group.Name, contacts);
return Page();
}
Views are XAML Page objects, and can optionally have a model. Here's an example:
The idea is that upon navigation, a controller is created, the action is executed, and the view and view model are created. The view then becomes the focus of the frame. Put simply, the view and viewmodel are stateful, and the controller is stateless.
Navigation between views (with nice transitions) can be done either programatically:
Navigator.For(Frame).NavigateWithTransition("Home", "Main", "ZoomOut");
Or through Blend behaviors:
The framework supports the ASP.NET MVC concepts of Action Filters, Model Binders, View Engines and more - I'll cover them in a later post.
Back to: Magellan Home
Welcome, my name is Paul Stovell. I live in Brisbane and work on Octopus Deploy, an automated deployment tool for .NET applications.
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.
Paul
Having just read a book on ASP MVC, this looks quite interesting. I could not see how the Model/ViewModel got associated with the View. But I like the idea a lot
Aha that is actually done in the Render method, nice one
Very. Cool.
Look good!
Didn't Magellan die on the way!
Looks good! I've never really liked how MVVM manages navigation, so this can be great.
Do you think this can handle a more LOB desktop app, where you can have several windows (or views) open at the same time?
Example, you go and start editing a contact, then you have to go to look for something else, but you're not finished with the editing. How do think Magellan could handle this?
Hi Eduardo,
The latest code has some integration with the Composite WPF library (Prism).
Navigation is scoped to a navigation service, and can also be executed without a navigation service. Your controllers can return:
- Pages (which will be shown in a navigation service)
- Windows (which will be shown as a new window/dialog)
- Views (which can be shown in a Prism 'region')
So when a contact is opened, one controller action would return an action result to the effect of "create a new Contact view, and show it in the Content region". Your Prism region adapter would add the item to a tab control. This way, you can have multiple objects open at once.
Now, within a tab, you might choose to have navigation frames. So one tab could be "pay bill". Within that tab you would navigate through the process of paying a bill, but you can have other tabs open with different tasks.
Once I flesh out some of the Prism integration I'll extend the sample applications to demonstrate this and write some more posts on it.
Paul
Thanks Paul. After writing the comment I was thinking about letting the user to have more frames (in the main window), so they can have multiple views at once, yet retaining the simpler web style navigation of your framework.
Three things:
Download and install KB958017 RollUp Hotfix for VS2008 if you want fix the pesky warning about Blend Interaction behaviors. http://code.msdn.microsoft.com/KB958017/Release/ProjectReleases.aspx?ReleaseId=1719
Paul: Where do you think is the right place to ask questions about Magellan? StackOverflow?
Do you know that http://magellan-project.org/ (used in your code) is taken a SAP product?
Paul:
Suppose that you are editing a contact.
You have the Save button with
<Button Content="Save" IsEnabled="{Binding CanSave}">
<i:Interaction.Behaviors>
<magellan:NavigateBehavior Action="Save" Controller="Contact" Transition="Forward">
<magellan:NavigateBehavior.Parameters>
<magellan:Parameter ParameterName="Contact" Value="{Binding Path=SelectedItem}"/>
</magellan:NavigateBehavior.Parameters>
</magellan:NavigateBehavior>
</i:Interaction.Behaviors>
</Button>
How do I code the controller side of this?
Public Function Save(ByVal Contact As Contact) As ActionResult
Try
Contact.Save() 'perform the DB saving
Return Index() 'Call other action result that brings the list of contacts?
Catch ex As Exception
'what to do here? return a dialog window? I tried but only ActionResult allowed
'Even if I managed to warn the user about the error, how to return a "DoNothing" result?
End Try
End Function
Maybe I'm expecting too much from the project, and have to mix it with regular WPF to do a real world app. You tell me.
@Eduardo: you're welcome to use StackOverflow or just post them as questions on this blog. I'll set up a bug tracker soon. I'll check the magellan name issue with the domain owner.
To make your example work, get the latest Magellan build. There are two view engines by default - one for pages and one for Windows. The second one will detect that your view is a window, and call Window.Show(). I plan to have an attribute or interface that allows the view to decide whether to be shown as a dialog or normal window, but if you like you can write your own ViewEngine (start with the WindowViewEngine).
Your controller in that example would look something like this:
Public Function Save(ByVal Contact As Contact) As ActionResult
Try
Contact.Save() 'perform the DB saving
Return Index() 'Call other action result that brings the list of contacts?
Catch ex As Exception
return View("Error")
End Try
End Function
In your project you could add an ErrorWindow.xaml which is just a regular Window (optionally implementing IView if you want to make use of a model). The ViewEngine will detect that it is a Window and will show it.
To answer your second question - how to tell it to do nothing - you can return a CancelResult like this:
Public Function Save(ByVal Contact As Contact) As ActionResult
Try
Contact.Save() 'perform the DB saving
Return Index() 'Call other action result that brings the list of contacts?
Catch ex As Exception
exceptionHandler.HandleException(ex)
return New CancelResult()
End Try
End Function
You can see how the Cancel action result is implemented here.
(I meant to add a convenience method to Controller to make this easy - i.e., return Cancel()
- I'll check that in tonight.
Hi Paul, it's me again :)
When you have this method in the controller
Public Function Save(ByVal Contact As Contact) As ActionResult
Try
Contact.Save() 'perform the DB saving
Return Index() 'Call other action result that brings the list of contacts
Catch ex As Exception
return View("Error")
End Try
End Function
- Is there a way that
Index
does not create another view, but navigate to the existing one (if exists)? - Is there a way to destroy a View (in this case, the contact view, which is not longer valid because the record is already saved in the DB)
Thanks again!
This is awesome! I can definitely see how this will make things easier for developers coming from ASP.NET MVC (like me). Fantastic job so far! :)
I've reposted the question in StackOveflow. http://stackoverflow.com/questions/1797307/wpf-magellan-is-there-a-way-to-navigate-to-a-existing-view
sacha barber