An Implementation of Castle Windsor Bootstrapper for Caliburn.Micro

This last week Jeff Schumacher and I have been working on a fairly complex set of business logic driving the entire design from the outside in with specification style unit tests. In an effort to help our tester get a head start validating the logic and assumptions in our code we promised to create a simple UI. Jeff has some WPF experience, so I suggested we do a simple WPF application and he can show me his “XAML Fu”. I have yet to get beyond playing with XAML, so having a little toy project and pairing with someone who has some experience already is a great way to get your feet wet. I also suggested we use Calibun.Micro and awesome little library Rob Eisenberg that brings convention over configuration for WPF applications to the table. So we pulled down the library and cracked open Rob’s Soup to Nuts series of tutorials.

In part two of his series, Rob describes how to override Caliburn.Micro’s default Bootstrapper with a custom implementation using the Managed Extensibility Framework. At work we use Castle Windsor, so we followed along with the tutorial injecting Castle magic at the appropriate spots.

To provide a custom bootstrapper for Caliburn.Micro, you simply inherit from the Bootstrapper where T is your shell view model and then override the GetInstance, GetAllInstances and BuildUp methods providing your own implementation. With the exception of the BuildUp method, they pretty much line up perfectly with Castle Windsor’s interface allowing you to simply wrap it. The BuildUp method is used to perform property injection on an already created instance, Castle Windsor does property injection by default on resolution of your types and does not explicitly expose a build up method. This is easily overcome by adding an extension method to the container to provide that service.

public static class WindsorExtensions
    {

        public static void BuildUp(this IWindsorContainer container, object instance)
        {
           instance.GetType().GetProperties()
                .Where(property => property.CanWrite && property.PropertyType.IsPublic)
                .Where(property => container.Kernel.HasComponent(property.PropertyType))
                .ForEach(property => property.SetValue(instance, container.Resolve(property.PropertyType), null));
        }
    }

The bootstrapper then looks like this:

public class CastleBootstrapper : Bootstrapper
    {
        private ApplicationContainer container;

        protected override void Configure()
        {
            container = new ApplicationContainer();
        }

        protected override object GetInstance(Type service, string key)
        {
            if (string.IsNullOrWhiteSpace(key))
                return container.Resolve(service);

            return container.Resolve(key);
        }

        protected override IEnumerable GetAllInstances(Type service)
        {
            return (IEnumerable) container.ResolveAll(service);
        }

        protected override void BuildUp(object instance)
        {
            container.BuildUp(instance);
        }
    }
}

The ApplicationContainer class is simply inherits from WindsorContainer, I like to separate out my component registration from my bootstrapping code. It looks something like this:

public class ApplicationContainer : WindsorContainer
    {
        public ApplicationContainer()
        {

            Register(
                    Component.For().ImplementedBy().LifeStyle.Is(LifestyleType.Singleton),
                    Component.For().ImplementedBy().LifeStyle.Is(LifestyleType.Singleton)
           );

            RegisterViewModels();
        }

        private void RegisterViewModels()
        {
            Register(AllTypes.FromAssembly(GetType().Assembly)
                         .Where(x => x.Namespace.EndsWith("ViewModels"))
                         .Configure(x => x.LifeStyle.Is(LifestyleType.Singleton)));
        }
    }

This code is all that is needed to get the sample application from the tutorials to work with Castle Windsor.

Follow me on Mastodon!