ISerialized .Net, C#, Scrum and agile software development

20Dec/0920

Getting started with Caliburn

The lack of documentation and examples of usage is one of the biggest obstacles getting started with Caliburn. I hope this article can help somewhat!

One of my biggest fears when starting a new project is heading in the wrong direction and not realizing that we are headed for disaster until it's too late. Due to architectural decisions, this is always a worry, but the new project is based on WPF, the risk and possibility of a failure is drastically increased. I'm afraid that my WPF projects become "Windows Formish", not taking advantage of MVP/ MVVM.

This is Part 1 of my series on Caliburn, Part 2 on multiple views on one ViewModel can be found here, and Part 3 on unit testing Caliburn can be found here.

Caliburn was designed to aid in the development of WPF and Silverlight applications, enabling easier use of among other MVP and MVVM. This article will be a walkthrough of a rather simple WPF application based on Caliburn. The example used here is a very simple application retrieving a value from a textbox, publishing it to an EventAggregator, and then let another component handle the published message. Hopefully I am able to show some of the advantages of Caliburn through this example.

To get started we create a new project called CaliburnCalculator, and add the following dlls as references:

  • Caliburn.Core
  • Caliburn.PresentationFramework
  • Caliburn.Unity
  • Microsoft.Practices.ObjectBuilder2
  • Microsoft.Practices.ServiceLocation
  • Microsoft.Practices.Unity
  • Microsoft.Practices.Unity.Configuration

[project.png]

The first three are the essential Caliburn dlls needed, whereas the rest is a part of Microsoft Composite Application Guidance.

We can delete the Window1.xaml, as we will create the application from scratch. The App.xaml and replace the whole file with the following:

<ApplicationModel:CaliburnApplication x:Class="CaliburnCalculator.App"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:ApplicationModel="clr-namespace:Caliburn.PresentationFramework.ApplicationModel; assembly=Caliburn.PresentationFramework">
</ApplicationModel:CaliburnApplication>

Here we say that instead of this being a System.Windows.Application this is of type CaliburnApplication. Before we go any further, we need a little more background info on this. In Caliburn the Views are considered a graph of ViewModel objects, so we will need a top level ViewModel to handle this. As far as I have read on several blogs, this top level ViewModel is often called the ShellViewModel. Caliburn will get hold of this when we override the default CreateRootModel, and return our own implementation of a ShellViewModel with a corresponding interface called IShellViewModel, used by the Unity pattern to get hold of an instance.

We will use the Concept of EventAggregators to handle the backing behind the ViewModels, so We will add an Class called EventAggregator and an Interface called IEventAggregator which has two methods, AddSubscriber and Publish (For more info on Event Aggregator pattern please see Martin Fowler and Microsoft Pattern and Practices Composite Application Guidance for WPF ). Here we will just make a very simple version of the Event Aggregator, as this is not the focus in this example.

using System.Collections.Generic;

namespace CaliburnCalculator.EventAggregator
{
	public interface IEventAggregator
	{
		void AddSubscriber(ISubscriber subscriber);
		void Publish(int message);
	}

	public class EventAggregator : IEventAggregator
	{
		private List<ISubscriber> subscribers;
		public void AddSubscriber(ISubscriber subscriber)
		{
			if (subscribers == null)
				subscribers = new List<ISubscriber>();
			subscribers.Add(subscriber);
		}

		public void Publish(int message)
		{
			subscribers.ForEach(x => x.HandleMessage(message) );
		}
	}

	public interface ISubscriber
	{
		void HandleMessage(int number);
	}
}

It would be fairly easy to introduce generics here, but for the simplicity of our example, we stick to only sending integers through a very simplified event aggregator!

In addition to, this Caliburn will need a unity adapter and a unity container to handle the instantiation of our ViewModels (For more information about unity, please visit the Composite Application Guidance for WPF for examples).

So our App.xaml.cs becomes the following:

using Caliburn.PresentationFramework.ApplicationModel;
using Caliburn.Unity;
using CaliburnCalculator.EventAggregator;
using CaliburnCalculator.ViewModels;
using Microsoft.Practices.ServiceLocation;
using Microsoft.Practices.Unity;

namespace CaliburnCalculator
{
	public partial class App
	{
		protected override IServiceLocator CreateContainer()
		{
			IUnityContainer container = new UnityContainer();
			container.RegisterInstance(EventAggregator());
			var adapter = new UnityAdapter(container);

			return adapter;
		}

		protected override object CreateRootModel()
		{
			var binder = (DefaultBinder)Container.GetInstance<IBinder>();

			binder.EnableMessageConventions();
			binder.EnableBindingConventions();

			return Container.GetInstance<IShellViewModel>();
		}

		public IEventAggregator EventAggregator()
		{
			var eventAggregator = new EventAggregator.EventAggregator();
			return eventAggregator;
		}
	}
}

Here we register an instance of our EventAggregator in CreateContainer which we override from CaliburnApplication, before we later use our unity container and adapter to create our root model in the CreateRootModel method which we override from CaliburnApplication. Notice here that we never really instantiate our ShellViewModel, we only tell or service locator to give us an instance of IShellViewModel, whether this will give us the same or a new instance each time we asks for an IShellViewModel is configured through Caliburn metadata tags in our actual ShellViewModel class.

We will now move over to the user controls, before we later come back and create a root model called ShellViewModel, to fill the gap and build our application window.

Until now we have not focused on what our application is all about, other than that it will be a Caliburn application. Our application will consist of two user controls one which handles the input, and one which will handle the result. We will input a number and press a button, and later calculate a result and present it in another user control. So our first user control only cares about input-data and don't care what happens down the line, and the other has no clue what so ever of where the data comes from, all it cares about is how its presented to the user. (I have intentionally left out the models here, as my focus here is to show how Caliburn handles interaction view-viewmodel, viewmodel-event aggregator and usage of unity)

We now create a new Folder in our project called Views, where we will put our user controls. We will here add two standard WPF user controls called NumberInputView and ResultView. In NumberInputView will add a textbox and a button.

 <UserControl
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="CaliburnCalculator.Views.NumberInputView"
	Height="109" Width="511" mc:Ignorable="d">
	<UserControl.Background>
		<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
			<GradientStop Color="Black" Offset="0"/>
			<GradientStop Color="White" Offset="1"/>
		</LinearGradientBrush>
	</UserControl.Background>
	<Grid Margin="0">
		<Border Margin="0,15.962,12,20.5" x:Name="border1" HorizontalAlignment="Right" Width="143" CornerRadius="10" BorderThickness="5">
			<Border.Background>
				<LinearGradientBrush MappingMode="RelativeToBoundingBox" EndPoint="0.5,1" StartPoint="0.5,0">
					<GradientStop Color="Black" Offset="1"/>
					<GradientStop Color="White"/>
				</LinearGradientBrush>
			</Border.Background>
			<Button x:Name="button1" BorderThickness="0" Foreground="White" FontWeight="Bold" FontSize="24" Margin="0" Content="Square it!" Background="{x:Null}">
				<Button.Effect>
					<DropShadowEffect/>
				</Button.Effect>
			</Button>
		</Border>
		<TextBox Margin="14,20.038,159,20.5" x:Name="textBox1" FontSize="24" BorderThickness="5" Text="Specify a number" d:LayoutOverrides="Width, HorizontalMargin" VerticalContentAlignment="Center">
			<TextBox.BorderBrush>
				<LinearGradientBrush MappingMode="RelativeToBoundingBox" EndPoint="0.5,1" StartPoint="0.5,0">
					<GradientStop Color="Black" Offset="1"/>
					<GradientStop Color="White"/>
					<GradientStop Color="#FF6A6A6A" Offset="0.581"/>
					<GradientStop Color="#FF878787" Offset="0.465"/>
				</LinearGradientBrush>
			</TextBox.BorderBrush>
		</TextBox>
	</Grid>
</UserControl>

Now we create our second user control which only contains two labels:

With the XAML:

<UserControl
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="CaliburnCalculator.Views.ResultView"
	Height="109" Width="511" mc:Ignorable="d">
	<UserControl.Background>
		<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
			<GradientStop Color="Black" Offset="1"/>
			<GradientStop Color="White"/>
		</LinearGradientBrush>
	</UserControl.Background>
	<Grid Margin="0">
		<Label HorizontalAlignment="Left" Margin="76,24.5,0,0" VerticalAlignment="Top" Content="The square is:" FontSize="32" FontWeight="Bold" Foreground="White" />
		<Label HorizontalAlignment="Left" Margin="342,24.5,0,0" VerticalAlignment="Top" Content="XX" FontSize="32" FontWeight="Bold" Foreground="White" />
	</Grid>
</UserControl>

Now that we have our Views (user controls) in place, we are ready to take a look at the ViewModels related to our Views. We create a new folder called ViewModels where we will add two classes:

  • NumberInputViewModel
  • ResultViewModel

Note: Here starts some of Caliburns magic. It's VERY important that you are consistent with your naming, as Caliburn automatically hooks up the View with the ViewModel  based on the class names. When I have two classes, one named ResultView and one named ResultViewModel, Caliburn will automatically hook these together as a View and a ViewModel of Result.

As described earlier, it's hard to ensure correct usage of the strengths of WPF programming, especially whenever new developers are introduced on a well-designed WPF project. This is why Caliburn is a very safe choice in WPF solution, as this guides us into a safe and sound design. To ensure correct data binding and correct thread-affinity of event publications, Caliburn has made this easy for us by adding a PropertyChangedBase which we can inherit. This class also implements INotifyPropertyChanged. In our example we will inherit the Presenter class which is one of the classes based on PropertyChangedBase.

With the EventAggregator and Presenter in place we have the necessary backing to working with the NumberInputViewModel. Since our view contains  a text box and a button, we need a property we call Number to hold the value, and a method we call SquareIt to handle the button click called SquareIt. Since this is a Caliburn application, we will also need one more method, namely CanSquareIt. For every method associated with a button, we need an extra method prefixed Can which is called to check whether the button can be enabled. In our case we don't evaluate anything up front, so we just return true.

Note: My design here as far as I can see is a MVVM, as I can have more than one view hooked up to one ViewModel (which is not the case in MVP with only one view per presenter), but still I inherit from Presenter!? If someone can clarify this in a comment, I would really appreciate it!
Our NumberInputViewModel then becomes:

using System;
using Caliburn.Core.Metadata;
using Caliburn.PresentationFramework.ApplicationModel;
using CaliburnCalculator.EventAggregator;

namespace CaliburnCalculator.ViewModels
{
	public interface INumberInputViewModel: IPresenter
	{
	}

	[PerRequest(typeof(INumberInputViewModel))]
	public class NumberInputViewModel: Presenter, INumberInputViewModel
	{
		private readonly IEventAggregator _eventAggregator;

		public NumberInputViewModel(IEventAggregator eventAggregator)
		{
			_eventAggregator = eventAggregator;
		}

		private string _number;
		public string Number
		{
			get { return _number; }
			set { _number = value; NotifyOfPropertyChange("Number");}
		}

		public bool CanSquareIt()
		{
			return true;
		}

		public void SquareIt()
		{
			_eventAggregator.Publish(Int32.Parse(Number));
		}
	}
}

There are a couple of things worth notice:

  • We have an empty interface based on the IPresenter interface needed by Caliburns UnityAdapter.
  • We add a PerRequest attribute to indicate how the unity should be handled. In this case it is a little irrelevant so just add a PerRequest indicating that it should return a new ViewModel each time it is asked for an instance of INumberInputViewModel.
  • The constructor of the ViewModel has an IEventAggregator as a parameter. This is handles by the unity container to inject the correct instance of  IEventAggregator as we already have seen, is the instance registered in App.xaml.cs
  • The setter of Number also calls the NotifyOfPropertyChaned inherited from Presenter, this will make the View update its value where bound to Number.
  • The CanSquareIt returns true. If we here changed this to false, the button would have been disabled at startup.
  • The SquareIt method publishes the value of our Number property to the event aggregator.

We can now move back to NumberInputView, look at the XAML and finalize the mapping between the View and ViewModel. We change the button name to SquareIt:

<Button Name="SquareIt" BorderThickness="0" Foreground="White" FontWeight="Bold" FontSize="24" Margin="0" Content="Square it!" Background="{x:Null}">

This will automatically hook up the button in the View to the SquareIt and CanSquareIt methods of the ViewModel. Whereas for the textbox we change the text property to bind to the Number property:

<TextBox Margin="14,20.038,159,20.5" x:Name="textBox1" FontSize="24" BorderThickness="5" Text="{Binding Number}" d:LayoutOverrides="Width, HorizontalMargin" VerticalContentAlignment="Center">

By changing the value of the text property, Caliburn will automatically bind this text box to the Number property of the associated ViewModel. Now we are actually finished with the binding between NumberInputView and NumberInputViewModel, Caliburn will take care of the rest!

Since this article focuses on the View ViewModel integration in Caliburn, we let the ResultView be the Subscriber to the NumberInput, in a real life setting however, the Result would rather be a subscriber to the actually result calculated further down the stack. So the message sent by NumberInputViewModel would be consumed by another class, which then sends a new message that ResultViewModel subscribes to. I might post an extended version later which describes this in more details.

With the simplifications described we can let the ResultViewModel subscribe to the simple EventAggregator described here. So the ResultViewModel will receive a message, which is an integer. We then square it and send a NotifyOfPropertyChanged, which will make the ResultView
automatically update to reflect the changes in the ViewModel.

So we create a new ResultViewModel class inheriting from Presenter, and also implementing the ISubscriber interface used to consume the message received. Giving us the following:

using System;
using Caliburn.Core.Metadata;
using Caliburn.PresentationFramework.ApplicationModel;
using CaliburnCalculator.EventAggregator;

namespace CaliburnCalculator.ViewModels
{
	public interface IResultViewModel : ISubscriber, IPresenter
	{

	}

	[PerRequest(typeof(IResultViewModel))]
	public class ResultViewModel : Presenter, IResultViewModel
	{
		public ResultViewModel(IEventAggregator aggregator)
		{
			aggregator.AddSubscriber(this);
		}

		private string _result;
		public string Result
		{
			get { return _result; }
			set { _result = value; NotifyOfPropertyChange("Result");}
		}

		public void HandleMessage(int number)
		{
			Result = Math.Pow(number, 2).ToString();
		}
	}
}

Notice that the constructor adds itself as a subscriber to the event aggregator. We have also added a Result property which our result label will bind to. When a message is received the ViewModel will process the message and update, Result property and notify that it has updated resulting in an automatic update of the View to reflect the changed ViewModel.

Once again, we just have to make a minor change to the XAML of the View, to enable the binding:

<Label HorizontalAlignment="Left" Margin="342,24.5,0,0" VerticalAlignment="Top" Content="{Binding Result}" FontSize="32" FontWeight="Bold" Foreground="White" />

All we do here is change the Content to a binding to Result, and Caliburn will make sure it is bound correctly.

The only part missing now is the ShellView and ShellViewModel to set it all up.
We first create the ShellView which will be very limited: A simple WPF Window with a StackPanel to stack up usercontrols. In the Views folder, create a new WPF Windows called ShellView, and replace the whole XAML with the following:

<Window x:Class="CaliburnCalculator.Views.ShellView"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:cal="clr-namespace:Caliburn.PresentationFramework.ApplicationModel;assembly=Caliburn.PresentationFramework"
	Title="ShellView" Height="248" Width="531">
	<Grid>
		<StackPanel Name="LayoutRoot" Background="LightGray">
			<ItemsControl ItemsSource="{Binding Presenters}">
				<ItemsControl.ItemTemplate>
					<DataTemplate>
						<ContentControl  cal:View.Model="{Binding}" ></ContentControl>
					</DataTemplate>
				</ItemsControl.ItemTemplate>
			</ItemsControl>
		</StackPanel>
	</Grid>
</Window>

Now we start adding the final glue to the Caliburn solution!  All we do here is allocated the space in the Window where Caliburn will control the content through the Presenters.  We let our ShellViewModel inherit MultiPresenter, which is one of several Presenters based on PresenterBase and PropertyChangedBase. By letting our ShellViewModel inherit MultiPresenter, we can easily stack up our two user control in, as shown in the source code of the ShellViewModel:

using Caliburn.Core;
using Caliburn.Core.Metadata;
using Caliburn.PresentationFramework.ApplicationModel;
using Microsoft.Practices.ServiceLocation;

namespace CaliburnCalculator.ViewModels
{
	public interface IShellViewModel
	{
	}

	[Singleton(typeof(IShellViewModel))]
	public class ShellViewModel: MultiPresenter, IShellViewModel
	{
		private readonly IServiceLocator _locator;

		public ShellViewModel(IServiceLocator locator)
		{
			_locator = locator;
		}

		public void Open<T>() where T : IPresenter
		{
			this.Open(_locator.GetInstance<T>());
		}

		public override void Activate()
		{
			Open<INumberInputViewModel>();
			Open<IResultViewModel>();
		}
	}
}

A couple of things to notice here:

  • We have an empty IShellViewModel to be used through the unity container
  • We have a singleton attribute to indicate that there should be only one instance of type IShellViewModel
  • Our constructor gets a service locator, namely Caliburns UnityAdapter, the same created in the App.xaml.cs from the CreateContainer method
  • The Activate method is called by Caliburn when it starts our ShellViewModel, and we ask the IServiceLocator to open one (or the) instance of the specified interfaces. The ServiceLocator is the Caliburns UnityAdapter created in App.xaml.cs from the CreateContainer method.
  • We do two Open calls here, and since we have a MultiPresenter, both Views are added to the presenter and shown.

That's all, and we are ready to hit F5!

This is Part 1 of my series on Caliburn, Part 2 on multiple views on one ViewModel can be found here, and Part 3 on unit testing Caliburn can be found here.

About Paul Eie

My name is Pål Eie (English alias: Paul Eie) I'm an IT consultant working in Stavanger, Norway. My experience spans from AIX to Windows and from embedded C to Uniface, Java, C++ and C#. My blog will involve everything that is related to technology. As long as it might be of interest to either newbies or seniors, I will blog about it! If you find some of my posts about basic old technology, it just means you know more than many of my other readers!
Comments (20) Trackbacks (0)
  1. Paul, thank zou for zour detailed introduction of caliburn! It's realy a good startup!
    I tried to get my first caliburn application running following your example, but unfortunately I get an empty window (and it is not the ShellView). During debugging I see in caliburns DefaultWindowManager that a ShellView has been created and the ShellViewModel.Activate is called.
    But an empty and untitled window opens.
    Any idea?

  2. Hi, I am glad it could help!
    Check the following:
    1. Verify that you have used the name CaliburnCalculator for the namespace, solution and App
    2. Check that CreateRootModel in your App is called
    3. Go through your viewmodels and views to verify that you have used the correct naming on namespaces, classes and interfaces.
    4. Add a break point in NumberInputViewModel constructor to verify that it is called

    If you go through all this and you still have a problem we should at least have ruled out problems with the unit adapter. If it still fails, could you check that shellView exists? and also post the content of shellView.xaml and ShellViewModel.cs here?

    Hope it helps!

  3. Sorry in my last comment unit adapter, is of course UnityAdapter! :)

  4. Hi Paul,
    Your post is a good startup for caliburn. I tried your example, but i am facing some issues while debugging.

    IEventAggregator and ISubscriber could not be found in any of the referenced dlls.

    It seems that CaliburnCalculator.EventAggregator class is missing and ISubscriber interface.

    Please help me out to solve this debugging issue.

    Thanks,
    $enthil

  5. Hi thanks for the good feed back!

    You will find the interfaces and class you ask for in the second code-listing in the post. Right after the text: "Here we will just make a very simple version of the Event Aggregator, as this is not the focus in this example"

    The event aggregator is just a simple implementation I wrote for this example.

    Just let me know if you still have problems with this!

    Best regards
    Paul

  6. Ya.. I have added the EventAggregator class. its working fine now Paul…

    Thanks
    $enthil

  7. Hi Paul,

    I am looking out for a good WPF framework and struggle over Caliburn. I don't try out the sample but I read the text carefully.

    Caliburn use some naming conventions to tie up the different parts, so we don't have to write some infrastructure description files in xml or whatever. Nice.
    But what is this naming convention, if you want more than one view associated to a Viewmodel? Or is the naming convention only good enough for simple apps and you have to do different steps to get a complex app running??

    Thanks for helping

    Gerhard

  8. Hi Paul,

    just another question.
    The shell window consists of a StackPanel which bounds to a collection holding 'Presenters'. Ok, this give me a lot of flexibility.
    But, I need something like an explorer window. The left pane holds a list of commands and the right pane (the content pane) shows different input masks or what ever. So its a MultiPresenter scenario, but you cant implement it using a single presenter collection …..

    Dis you?

    With best regards

    Gerhard

  9. Hi Gerard!
    Caliburn can be setup with more than one View per ViewModel (as expected with MVVM), through use of View.Context. I plan to add another blog post describing this in more detail, but I have not had time to do this yet!

    Regarding the StackPanel, I only use StackPanel for example purposes.

    Hope this helps!

    Paul

  10. Thanks for this, but could you please include the source code of this project?

    Thanks

    Thomas

  11. Hi Thomas!
    I will add the source code as soon as possible! I’ll post and update when everything is in place!

    Cheers
    Paul

  12. thanks a lot for the source code, very helpful!

  13. Very useful indeed …
    Is it possible to have the same scenario for silver-light 3 !!

    Regards.

  14. Hi!
    As far as I have understood, most of the features are Available in Silverlight as well, but there are some limitations. Take a look at the discussions on caliburn.codeplex.com !

    Cheers
    Paul!

  15. Fantastic tutorial. Helps newbies like me :D
    Thanks a lot!

  16. Hi
    I want to use caliburn. Currently I have my view and view model in different assemblies . What special care will I need to take care when I am having this kind of project structure ?

  17. I’m not quite sure about that. I have never testes that design. I recommend the forum at caliburn.codeplex.com if it doesnt work straight forward with your setup. Remember that some of Caliburns strength is convention over configuration so I recommend changing your your setup/configuration instead of tweaking the framework too much!

  18. I’v started learned caliburn, but, even in your example –

    Error 2 The type ‘ApplicationModel:CaliburnApplication’ was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built. D:\Install\CaliburnCalculator\CaliburnCalculator\CaliburnCalculator\App.xaml 1 2 CaliburnCalculator

  19. Hi!
    Try download the full source code here:
    http://filehub.iserialized.com/

    Check your naming of class and namespace in App.xaml and App.xaml.cs, might be something like that!
    Cheeers!

    Paul


Leave a comment


No trackbacks yet.

Blogglisten