I’ve released a new version of my Presentation and UI Composition toolkit: Radical.Windows.Presentation, the new feature is the first drop of the Castle Windsor integration package (Radical.Windows.Presentation.CastleWindsor), this first drop targets only WPF 4.0 (but it is a first drop, next to come will be WinRT, Silverlight 5.0 and Windows Phone 7.5).

The integration package simply adds support for the famous Invention of Control toolkit introducing the WindsorApplicationBootstrapper<TShellView> that is the concrete implementation of the abstract ApplicationBootstrapper base class. The role of bootstrapper is:

  • attach the relevant application events;
  • wire up stuff to configure the I.o.C. container;
  • show the shell view (the application main window);

Frictionless: convention based

As I have already said my first aim is to remove friction, it is not always easy and cannot be done every single time, but one thing that can give a lot of benefits in this area is to move from a configuration based toolkit to a convention based toolkit, I suppose that this concept is widely accepted and is nothing new.

What happens when this lines of code are executed:

namespace Radical.Presentation.Samples
{
	public partial class App : Application
	{
		public App()
		{
			var bootstrapper = new WindsorApplicationBootstrapper<MainView>();
		}
	}
}

A lot of things:

  1. The application Startup event is wired;
  2. When the Startup event is fired:
    1. The Inversion of Control container is created;
    2. The MEF composition container is created;
    3. The composition container is composed against the bootstrapper itself (here is why, in Italian unfortunately);
    4. The Inversion of Control container is configured using the bootstrap conventions;
    5. The main window (the one identified by the TShellView generic parameter) is resolved and shown;

Bootstrap conventions

Bootstrap conventions are mainly related to the way components are registered into the container:

  • every class the is defined in a namespace ending with “Services” (*.Services) will be considered a service and will be registered as singleton using as the service contract the first interface, if any, otherwise using the class type;
  • every class the is defined in a namespace ending with “Presentation” (*.Presentation) and whose type name ends with “ViewModel” (*ViewModel) will be considered as a view model and registered as transient;
    • following the same logic every type in the same namespace whose name ends with “View” (*View) will be considered a view, a transient view;
    • if a view or a view model are a shell, type name beginning with Shell* or Main*, they will be registered as singleton
    • be default views and view models will be registered using as service contract the class type and no interface is searched along the way;
  • every type defined in the “Messaging.Handler” namespace (*.Messaging.Handler) will be considered a message broker message handler and will be registered as singleton and automatically attached, as an handler, to the broker pipeline;

These are the main conventions used at boot time, there are a few more but less important. Obviously all these behaviors can be replaced or extended to accomplish the end user needs:

public App()
{
	var bootstrapper = new WindsorApplicationBootstrapper<MainView>();
 
	var conventions = bootstrapper.GetService<BootstrapConventions>();
 
	var original = conventions.IsService;
	conventions.IsService = t =>  {
		if( /* your logic to determine if “t” is a service */ )
		{
			return true;
		}
 
		return original( t );
	};
}

All the conventions are Func(s) or Predicate(s) and can be easily replaced as shown in the above sample, here is the list of the currently defined and used conventions:

  • IsService: determines if a type should be considered a service or not;
  • SelectServiceContracts: given the service type returns the list of contracts that should be used to register the given service;
  • IsMessageHandler:determines if a type should be considered a message handler or not;
  • SelectMessageHandlerContracts: given the message handler type returns the list of contracts that should be used to register the given message handler;
  • IsView: determines if a type should be considered a view or not;
  • IsViewModel: determines if a type should be considered a view model or not;
  • IsShellView: determines if a type should be considered a shell view or not;
  • IsShellViewModel: determines if a type should be considered a shell view or not;
  • SelectViewContracts: given the view type returns the list of contracts that should be used to register the given view;
  • SelectViewModelContracts: given the view model type returns the list of contracts that should be used to register the given view model;

In the next post will deal with runtime conventions.

.m