Magellan and IOC/DI Containers

Back to: Magellan Home

Magellan was designed to work without an Inversion of Control or Dependency Injection container, to keep it simple and accessible. However, as applications become more complicated, modern WPF applications can benefit immensely from IOC containers.

Magellan's extensibility points make using a container easy. Magellan resolves and instantiates two main types of objects - controllers and views. The out of the box implementation uses conventions, but they can be overridden.

To take control of resolving controllers, implement the IControllerFactory interface. Here is an example using Ninject:

public class NinjectControllerFactory : IControllerFactory
{
    private readonly IKernel _container;

    public NinjectControllerFactory(IKernel container)
    {
        _container = container;
    }

    public IController CreateController(NavigationRequest request, string controllerName)
    {
        var controller = _container.Get<IController>(controllerName);
        return new ControllerFactoryResult(controller);
    }
}

The ControllerFactoryResult has a second overload that also allows you to pass a callback that is invoked when the request has been processed - this can be used for disposing the controller if required.

Then assign it as the default controller factory:

var container = new StandardKernel();
container.Bind<IController>().To<HomeController>().Named("Home");
container.Bind<IController>().To<SettingsController>().Named("Settings");
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory(container));

When a navigation request is processed, the Navigator will consult the ControllerBuilder.Current for the controller factory. It will use this to ask the factory for a controller, then execute the controller. The ReleaseController method will be called as soon as the navigation request has completed.

Typically, each navigation will create a new controller - if you want to use the same controller, you could have your controller factory recycle the objects, or make them singletons.

The second object that Magellan creates is views. This is done through the IViewActivator implementation. The default implementation looks for a public, parameterless constructor.

Again, let's use Ninject to control view instantiation.

public class NinjectViewActivator : IViewActivator
{
    private readonly IKernel _container;

    public NinjectViewActivator(IKernel container)
    {
        _container = container;
    }

    public object Instantiate(Type viewType)
    {
        return _container.Get(viewType);        
    }
}

We then need to tell the view engines that it is available. Since the view engines are automatically registered, we need to manually re-register them:

var activator = new NinjectViewActivator(container);
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new PageViewEngine(activator));
ViewEngines.Engines.Add(new WindowViewEngine(activator));

If you are using Magellan with Composite WPF, you will also need to register the region view engine:

ViewEngines.Engines.Add(new CompositeViewEngine(activator));

Back to: Magellan Home

Last revised: 30 Dec, 2009 04:27 AM History

Discussion

10 Nov, 2009 12:48 AM

Hi Paul,

Nice article.

Just as an idea: you could possibly use Common Service Locator (http://www.codeplex.com/CommonServiceLocator) instead of NinjectViewActivator : IViewActivator (and possibly instead of IControllerFactory as well). Most of containers support CSL and it could give Magellan easier container abstraction options.

I'd also like to share how the same registration would look like with Unity and my auto registration extension (http://autoregistration.codeplex.com/) to it:

var container = new UnityContainer(); container .ConfigureAutoRegistration() .IncludeAllLoadedAssemblies() .Include(If.Implements, Then.Register().WithPartName(WellKnownAppParts.Controller)) .ApplyAutoRegistration();

This allows to register all Magellan controllers (Home, Settings, etc) by their names without having to list them explicitly.

10 Nov, 2009 12:54 AM

Sorry for repeating, just to make previously posted code to look better:

var container = new UnityContainer();
container
   .ConfigureAutoRegistration()
   .IncludeAllLoadedAssemblies()
   .Include(If.Implements, Then.Register().WithPartName(WellKnownAppParts.Controller))
   .ApplyAutoRegistration();

10 Nov, 2009 04:11 AM

Hi Artem, thanks for the comment. In a section on Magellan with Composite WPF (which I didn't publish to RSS yet, but you can see it online), I include an example of a ServiceLocatorControllerFactory and ServiceLocatorViewActivator. I added those primarily to support Composite WPF, but as you say they can be used to forward onto any IOC container.

I did bundle the service locator adapters into Magellan.Composite.dll; do you think it would be better to seperate this into a different assembly for those that want to use CSL but not Composite WPF?

Your suggestion to use conventions to register controllers automatically is a good one and should be considered by anyone who wants to create a low-friction developer experience.

10 Nov, 2009 04:16 AM

Dropping IViewActivator completely in favour of CSL would make sense but it worries me. First, it would require you to reference CSL even if you had no intention of using it, which would be a shame. Second, it would require the user to use a container, which I don't think should be a requirement (it should certainly be possible, just not required). So I'm playing it safe by having a Magellan interface and then an implementation that talks to CSL for those that can use it.

10 Nov, 2009 04:59 AM

Hi Paul,

I see what you mean and agree with your points about cons of dropping IViewActivator completely in favour of CSL. It makes sense to have Magellan interfaces and CSL implementation (in a separate assembly for those who want to use IoC via CSL and not Composite WPF but say Caliburn or another framework). This way you would allow to use most of containers with Magellan without having to implement anything (Magellan has CSL implementation of interfaces it works with, most of containers have CSL implementations).

Your Comments

Used for your gravatar. Will not be public.

Posting code? Indent it by four spaces to make it look nice. Learn more about Markdown.

Preview