Category Archives: Caliburn Micro

Using Common.Logging with Caliburn Micro

Caliburn Micro offers several extension points into it, one of which is for logging. For example

LogManager.GetLog = type => new MyLogManager(type);

LogManager.GetLog is a Func and allows us to supply our own logging mechanism.

As you can see, Caliburn Micro includes the interface ILog (the second generic parameter in the Func) for us to implement our own logging code. However I also rather like using the Common.Logging library to abstract the specific logger (i.e. NLog or log4net etc.) from my logging code. So how can we implement our logging using the Common.Logging ?

We just need to implement our own Caliburn Micro ILog and redirect it to the Common.Logging framework. For example

using CommonLogging = global::Common.Logging;
public class CommonLogManager : Caliburn.Micro.ILog
{
   private readonly CommonLogging.ILog log;

   public CommonLogManager(Type type)
   {
      log = CommonLogging.LogManager.GetLogger(type);
   }

   public void Error(Exception exception)
   {
      log.Error("Exception", exception);
   }

   public void Info(string format, params object[] args)
   {
      log.Info(String.Format(format, args));
   }

   public void Warn(string format, params object[] args)
   {
      log.Warn(String.Format(format, args));
   }
}

The code is a little convoluted with namespaces due to ILog existing in both Caliburn Micro and Common.Logging, but unfortunately being different types.

Now just place the following code into the Configure method on your implementation of a Caliburn Micro Bootstrapper

LogManager.GetLog = type => new CommonLogManager(type);

Dynamically loading assemblies into Caliburn Micro

I want to be able to load the views/viewmodel assemblies into Caliburn Micro dynamically, i.e. instead of adding references to them to the project….

Well, we’ve got two ways of loading assemblies (that I’m currently aware of). The first is via the Bootstrapper and the second can be handled pretty much anywhere. Both examples below are shown using the DLL name, but ofcourse we could take this further via configuration files, build out own attributes and so on, but I leave that decision to the user.

So through Bootstrapper simply overload SelectAssemblies, for example

protected override IEnumerable<Assembly> SelectAssemblies()
{
   string path = Path.GetDirectoryName(
                    Assembly.GetExecutingAssembly().Location);
   return base.SelectAssemblies().Concat(
         new Assembly[] 
         { 
            Assembly.LoadFile(Path.Combine(path, "MyViews.dll"))
         });
}

However if you’re using a plugin approach, possibly where you allow the user to select a plugin DLL or the likes, then at the point you want to load the assembly you could write something like the following

string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
Assembly a = Assembly.LoadFile(Path.Combine(path, "Rabbit.Controls.dll"));
AssemblySource.Instance.Add(a);

The key here is the AssemblySource singleton and adding assemblies to the IObservableCollection Instance.

Binding to a listbox and selected item using Caliburn Micro

This will work with a listbox or combo box.

So, using Caliburn Micro we use convention based naming for the ItemsSource as follows

<ListBox x:Name="Employees" />

But as you see there’s nothing for SelectedItem, instead it is taken that the property name in the view model for the selected item will be SelectedEmployee, i.e.

public class EmployeesViewModel : PropertyChangedBase
{
   private EmployeeViewModel selected;

   public ObservableCollection<EmployeeViewModel> Employees { get; private set; }

   public EmployeeViewModel SelectedEmployee
   {
      get { return selected; }
      set
      {
         if (selected != value)
         {
            selected = value;
            NotifyOfPropertyChange();
         }
      }
   }
}

Caliburn Micro’s message bus (event aggregator)

Most MVVM frameworks come with some form of message bus. A means to send messages from one class to any classes wishing to listen for such messages. Ofcourse in a MVVM world those classes might be view models also.

In Caliburn Micro this message bus implementation is known as the event aggregator which is defined by the IEventAgreggator interface.

A class wishing to subscribe to events simple get passed an IEventAggregator as calls the subscribe method as follows

public MyViewModel(IEventAggregator eventAggregator)
{
   eventAggregator.Subscribe(this);
}

In this example we’re using the constructor and in this case, I’m using the Ninject bootstrapper which we looked at in a previous post, to inject the IEventAggregator into the constructor.

Obviously we need to add some code to actually handle any events as this solely subscribes to the aggregator, so for the sake of argument we’ll assume we have an event class which looks like the following

public class MyMessageEvent
{
   public MyMessageEvent(string message)
   {
      Message = message;
   }

   public string Message { get; provate set; }
}

And now on our view model we implement IHandle

public class MyViewModel : PropertyChangedBase, IHandle<MyMessageEvent>
{
   public void Handle(MyMessageEvent message)
   {
      // do something with the event
   }
}

Now we need something to publish events…

If we create another view model and again pass the IEventAggregator into the constructor as per

public class MyViewModel : PropertyChangedBase
{
   private IEventAggregator eventAggregator;

   public MyViewModel(IEventAggregator eventAggregator)
   {
      this.eventAggregator = eventAggregator;
   }

   public void PublishMessage()
   {
      event.Publish(new MyMessageEvent("Hello"));
   }
}

Caliburn Micro, convention based binding

Caliburn Micro implements a convention based system for binding to “default” properties on controls and actions etc. This is on by default but can be switched off using ViewModelBinder.ApplyConventionsByDefault and setting this to false. You can also enable/disable conventions on a view-by-view basis by setting the attached property View.ApplyConventions to true in a view, for example (with other namespace etc. removed)

<UserControl x:Class="Sample.ShellView"
   xmlns:Micro="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
   Micro:View.ApplyConventions="False">

Let’s take a look at some basics of the convention based code.

In a view model class (assuming you’re derived from PropertyChangedBae) we can add the following code

private string fullName;

public string FullName
{
   get { return fullName; }
   set
   {
      if(fullName!= value)
      {
         fullName= value;
         NotifyOfPropertyChange(() => FullName);
      }
   }
}

Now, in the corresponding view add the following

<TextBox Name="FullName" />

Note the use of the name property and no binding property, this is because Caliburn Micro creates the link for us based upon the name and the binding property defined within it for mapping within Caliburn Micro (in other words, it’s binding based upon a naming convention).

For example the convention for a TextBox is that the binding property to be used is the Text property. Internally this looks like this

AddElementConvention<TextBlock>(TextBlock.TextProperty, "Text", "DataContextChanged");

We can, in fact, add our own convention based bindings for any controls not supported by default such as our own controls. For example, let’s create a convention based binding for a Rectangle object (which isn’t one of the default supported controls) whereby the convention based binding will look like this in XAML.

<Rectangle Width="100" Height="100" Name="Colour" />

and in our code (somewhere such as the top most view model or boostrapper) we would have something like this

ConventionManager.AddElementConvention<Rectangle>(Shape.FillProperty, "Fill", "FillChanged");

This will then create a binding from the name “Colour” on our viewmodel to the Fill property of the Rectangle (or Shape). Thus the Colour (obviously assuming it is a Brush) will be bound to the Fill property automatically.

The last argument in the AddElementConvention method is for the eventName and this can be used to use the same mechanism to fire commands on our view model.

Let’s add a Button to our code that looks like this, in XAML

<Button Name="Save" Content="Save" />

Now in the associated view model simply add the following code

public void Save()
{
}

It’s as simple as that to bind to the click event of the button. So, when the button is clicked the Save method on the view model is called. This is because of the following code, which Caliburn Micro

AddElementConvention<ButtonBase>(ButtonBase.ContentProperty, "DataContext", "Click");

One very useful bit of code in Caliburn Micro is for situations where you might wish to pass an argument to a method in a view model. Let’s assume that you have multiple save buttons on a form, one for draft save and one for full save (for want of a better example).

We want to use the same code on the view model to handle both saves, however depending on the argument pass to the method we will do a draft of full save. Maybe these two operations share a bunch of code so it suits us to reuse this code. Okay so it’s a slightly convoluted example, but you get the idea.

So our method will look like this

public void Save(string draftOrFull)
{
   // do a load of shared things

   if(draftOrFull == "draft")
   {
      // draft save
   }
   else if(draftOrFull == "save")
   {
      // full save
   }
}

Now add the following code to the XAML

<Button Content="Draft" cal:Message.Attach="[Event Click] = [Action Save('draft')]"/>
<Button Content="Save" cal:Message.Attach="[Event Click] = [Action Save('full')]"/>

The attached property Message.Attach will attach the Click event to the action “Save” passing the string draft or full – note the single quotes for the string.

We can achieve the same as above in a longer format as follows

<Button Content="Draft">
   <i:Interaction.Triggers>
      <i:EventTrigger EventName="Click">
         <cal:ActionMessage MethodName="Save">
            <cal:Parameter Value="draft" />
         </cal:ActionMessage>
      </i:EventTrigger>
   </i:Interaction.Triggers>
</Button>

Caliburn Micro and inversion of control using Ninject

Caliburn Micro comes with it’s own built in mechanism for creating objects as and when required. However it’s bootstrapper comes with methods which allow us to override the default behaviour. The methods such as GetInstance, GetAllInstances and BuildUp are used to resolve dependencies in a user supplied IoC container.

I’m the built in mechanism is more than adequate for most peoples usage, but I tend to rather like Ninject. So here are the steps (which ofcourse can be used with your own preferred IoC framework).

Create a bootstrapper as per the following (where ShellViewModel is replaced with your view model name)

public class AppBootstrapper : Bootstrapper<ShellViewModel>
{
   protected override void Configure()
   {
   }

   protected override void OnExit(object sender, EventArgs e)
   {
   }

   protected override object GetInstance(Type service, string key)
   {
   }

   protected override IEnumerable<object> GetAllInstances(Type service)
   {
   }

   protected override void BuildUp(object instance)
   {
   }
}

These are the methods we need to override and implement the code for, to allow Caliburn Micro to use our preferred Ioc framework.

Configure

The configure method is used to configure Caliburn Micro to use our IoC framework, so basically this is where we instantiate the StandardKernel in Ninject. Firstly add a class level variable as follows

private IKernel kernel;

Next override the Configure method to both create the kernel and set-up default bindings

protected override void Configure()
{
   kernel = new StandardKernel();

   kernel.Bind<IWindowManager>().To<WindowManager>().InSingletonScope();
   kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();
}

We’re going to want to have access to the WindowManager and EventAggregator within our code, so we’ll set up the bindings for them. If we’re passing a class to the generic argument of the Bootstrapper this this is enough code for the Configure method, but if, as I often prefer, we have something like

public class AppBootstrapper : Bootstrapper<IShellViewModel>

i.e. an interface passed as the generic argument, then we need to also set up the bindings within Ninject to resolve this interface. Hence adding the line

kernel.Bind<IShellViewModel>().To<ShellViewModel>().InSingletonScope();

to the end of the Configure method.

OnExit

Now we’ve created the instance of the kernel, the OnExit method allows us to place cleanup code such as

protected override void OnExit(object sender, EventArgs e)
{
   kernel.Dispose();
   base.OnExit(sender, e);
}

This is (as you can see from the arguments, a event handler that Caliburn Micro hooks up to the Application.Exit event.

GetInstance

This must be overridden when providing our own IoC container. The method is used get the service for a given service, so we can simply call the following

protected override object GetInstance(Type service, string key)
{
   if (service == null)
      throw new ArgumentNullException("service");

   return kernel.Get(service);
}

GetAllInstances

This method must be overridden when supplying our own IoC container and is used to get all instances of a service. We can override it thus

protected override IEnumerable<object> GetAllInstances(Type service)
{
   return kernel.GetAll(service);
}

BuildUp

Finally, and again required when supplying our own IoC container, we need to override the BuildUp method. This is used inject instances into the IoC container and can be written as

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

Full Code

The full code for this is as follows

public class AppBootstrapper : Bootstrapper<ShellViewModel>
{
   private IKernel kernel;

   protected override void Configure()
   {
      kernel = new StandardKernel();

      kernel.Bind<IWindowManager>().To<MetroWindowManager>().InSingletonScope();
      kernel.Bind<IEventAggregator>().To<EventAggregator>().InSingletonScope();

      kernel.Bind<IShellViewModel>().To<ShellViewModel>().InSingletonScope();
   }

   protected override void OnExit(object sender, EventArgs e)
   {
      kernel.Dispose();
      base.OnExit(sender, e);
   }

   protected override object GetInstance(Type service, string key)
   {
      if (service == null)
         throw new ArgumentNullException("service");
			
      return kernel.Get(service);
   }

   protected override IEnumerable<object> GetAllInstances(Type service)
   {
      return kernel.GetAll(service);
   }

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

Beginning with Caliburn Micro

Let’s take a look at some of the basics of Caliburn Micro.

Let’s start with the bare minimum, skeleton app to get us started…

  1. Create a WPF application is Visual Studio
  2. Use NuGet to add Caliburn.Micro via the references context menu
  3. Open App.xaml and remove the StartupUri=”MainWindow.xaml”
  4. Delete the file MainWindow.xaml as Caliburn Micro will create the main window for our application
  5. Caliburn Micro uses naming convention to allow it to load the relevant view for the view model, so the view should be prefixed with the same name as the viewmodel, i.e. create a class named ShellViewModel which will become the entry point to the application. Then create a UserControl named ShelllView
  6. Open ShellViewModel and drived the class from PropertyChangedBase (ass the using clause for Caliburn.Micro).

    PropertyChangedBase gives us the implementation of INotifyPropertyChanged for the databinding. So the code should look like

    public class ShellViewModel : PropertyChangedBase
    {
    }
    

    Obviously don’t forget to add Caliburn.Micro as a using clause

  7. Open the ShellView.xaml file and (just so we can easily see the user control’s usage, change the Background to a colour of your choosing. Also changed the Width and Height to a sensible starting value otherwise the application will open just as the caption bar. The user control size gives the initial size of the main window when it’s displayed
  8. Now we need to create the code to actually create the main window and hook display our intial control. Create a new class, it can be named anything you like but I’ll named mine AppBootstrapper to fit in with the App.xaml name
  9. Derive AppBootstrapper from Bootstrapper as per
    public class AppBootstrapper : Bootstrapper<ShellViewModel>
    {
    }
    

    Obviously don’t forget to add Caliburn.Micro as a using clause.

    As you’ll see we set the Bootstrapper up with the view model, not the view. Caliburn Micro’s naming conventions will work out the view name as ShellView and handle the composition of the views from this.

  10. At this point we have all the bits for Caliburn Micro to work but we need to actually get the bootstrapper to load. The simplest way is to edit App.xaml and enter the following code between the Application.Resources

    <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
    <ResourceDictionary>
    <local:AppBootstrapper x:Key="bootstrapper" />
    </ResourceDictionary>
    </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

    Obviously added the namespace as required.

Now if you run this you should see an application window with the dimensions based upon the size of the ShellView control and if you chose a different colour background this should be displayed.