Caliburn Micro Part 5: The Window Manager

Hello again and welcome to the next post in our Caliburn Micro tutorial series. A relatively simple tutorial this time where we will be looking at the Window Manager. Before we begin, here are the links to the previous tutorials in this series:

Part 1: Getting Started
Part 2: Data Binding and Events
Part 3: More About Events and Parameters
Part 4: The Event Aggregator

Many of you who have played around with Caliburn Micro will know there is not much mentioned about the Window Manager. Due to this, I won’t be covering everything about the Window Manager, I’ll simply explain what I know about it. To demonstrate how to use the Window Manager, we will be extending the application we made in the first blog post of this series.

Using a Window Manager

You may remember from the getting started blog post that one of the first things we did was delete MainWindow.xaml from the project. Caliburn Micro takes care of initializing the window, setting its data context and displaying the appropriate view for us. The Window Manager is one of the mechanisms responsible for getting this done. When you run an application built with Caliburn Micro, the Window Manager is automatically used to create the startup window. For small applications, this is all you really need to know about the Window Manager. When you are building larger applications that need to display other windows or dialogs, it’s time to learn how to use the Window Manager. To demonstrate this we will add a button to the application which will open a new window when clicked. Start by adding a button to AppView.xaml and hooking the click event to a method in AppViewModel.cs. You can do this using Caliburn Micro conventions as explained in the previous tutorials. I have called this method “OpenWindow”. In the OpenWindow method we are going to need access to an instance of a Window Manager. Although we could simply create a new instance of WindowManager and use that, it is best practice to get a hold of the global Window Manager instance that Caliburn Micro makes available to the application. We can do this by making a constructor on AppViewModel.cs that takes in an IWindowManager and store it in a field. You may remember from part 4 of this blog series of what needs to be done when you create a constructor on a view model that has at least 1 parameter. Here is a recap in 3 easy step:

1. Update the bootstrapper as follows. Remember to add System.ComponentModel.Composition.dll as a reference in your project.

using Caliburn.Micro;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
 
public class AppBootstrapper : Bootstrapper<AppViewModel>
{
  private CompositionContainer container;
 
  protected override void Configure()
  {
    container = new CompositionContainer(new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
 
    CompositionBatch batch = new CompositionBatch();
 
    batch.AddExportedValue<IWindowManager>(new WindowManager());
    batch.AddExportedValue<IEventAggregator>(new EventAggregator());
    batch.AddExportedValue(container);
 
    container.Compose(batch);
  }
 
  protected override object GetInstance(Type serviceType, string key)
  {
    string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
    var exports = container.GetExportedValues<object>(contract);
 
    if (exports.Count() > 0)
    {
      return exports.First();
    }
 
    throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
  }
}

2. Use the Export attribute on the AppViewModel class:

[Export(typeof(AppViewModel))]
public class AppViewModel : PropertyChangedBase
{
  ...
}

3. Use the ImportingConstructor attribute on the AppViewModel constructor:

private readonly IWindowManager _windowManager;
 
[ImportingConstructor]
public AppViewModel(IWindowManager windowManager)
{
  _windowManager = windowManager;
}

Now we can use the Window Manager instance in our OpenWindow method to open a new window. To keep this tutorial simple, we will simply create a new window that also uses the AppViewModel as the data context. This is done using the following code:

public void OpenWindow()
{
  _windowManager.ShowWindow(new AppViewModel(_windowManager));
}

Run this up and press the button to see another window appear. All we needed to do was pass in an instance of a view model, then everything else from creating the window instance and displaying the appropriate view for our view model is done for us. Once again Caliburn Micro makes our life easier!

Using the Window Manager in Caliburn Micro

The Window Manager has all sorts of methods and method overloads for opening windows, dialogs and popups. All these methods are fairly straight forward and easy to use. You can see more usage of the Window Manager in the HelloWindowManager sample that comes with Caliburn Micro. One more thing I wanted to look at is the ‘settings’ parameter. Here you can pass in a dynamic object used to set properties on the new window. This gives you fine-grained control on the appearance of your application if you need it. Here is an example where I am setting the WindowStartupLocation to be manual rather than center-owner.

public void OpenWindow()
{
  dynamic settings = new ExpandoObject();
  settings.WindowStartupLocation = WindowStartupLocation.Manual;
 
  _windowManager.ShowWindow(new AppViewModel(_windowManager), null, settings);
}

Custom Window Managers

There are scenarios where it is useful to implement a custom Window Manager. This is good if you need to set properties on all Window instances to be common values. For example, properties could include the icon, the window state, window size and applying custom chrome. The most useful property that I have found to set on windows is the SizeToContent property. By default, Caliburn Micro sets this to be SizeToContent.WidthAndHeight. This means that the window automatically sizes itself based on its content. Although this can be convenient at times, I have found this to cause some issues with certain application layouts and seems to be buggy when setting the window to be maximized by default. Creating a custom Window Manager is very simple. Start by adding a class that extends WindowManager. Next, you can override the EnsureWindow method and do something like the following:

protected override Window EnsureWindow(object model, object view, bool isDialog)
{
  Window window = base.EnsureWindow(model, view, isDialog);
 
  window.SizeToContent = SizeToContent.Manual;
 
  return window;
}

In this method, we start by calling base.EnsureWindow() to create the window instance. Next you can set any properties on the window that you want, and then simply return the window instance. The last step is to set an instance of your custom Window Manager to be globally used by the application. In the first code snippet in this blog post we make modifications to the bootstrapper. Here you can see we are adding a WindowManager instance to the CompositionBatch. You can replace this to use an instance of your custom window manager instead which would look like this:

batch.AddExportedValue<IWindowManager>(new AppWindowManager());

That’s everything I wanted to cover with the Window Manager, I hope you found it useful in building great WPF applications powered by Caliburn Micro! I appreciate your comments and feedback on the series!

Download the full Visual Studio 2010 project for this tutorial.

Next time I’ll give you an introduction to screens and conductors.

Happy coding :)

Tagged as WPF

Caliburn Micro Part 4: The Event Aggregator

For those of you who are new to this tutorial series, we have been learning about how to use Caliburn Micro to build WPF application with a robust MVVM architecture. Caliburn Micro is a framework used to help build .NET applications (WPF, Silverlight and Windows Phone 7) using several popular UI patterns including MVVM, MVP and MVC. It provides lots of neat ways to reduce the amount of work you need to do for common tasks such as setting up bindings and hooking up events. The various features of Caliburn Micro make it easy to have a clean line between the model objects and the UI. That means your application is easy to test and maintain. Here are links for the previous blog posts:

Part 1: Getting Started
Part 2: Data Binding and Events
Part 3: More About Events and Parameters

In this week’s tutorial we will be learning about how to use the event aggregator included with Caliburn Micro. The event aggregator is a service that makes it easy for multiple parts of your application to send messages to each other. This is useful when your application is made up of several view-models that need to communicate. To do this, you subscribe objects (such as view models) to the event aggregator and specify what type of message they should listen for. You also define what the object does when it receives such messages. Then, when another part of the application publishes a message, the event aggregator makes sure the appropriate subscribed objects receive it and perform the appropriate action. Throughout this tutorial we will be extending the application we made in the getting started tutorial. (You can download the application here.)

Although the event aggregator is more useful for larger applications that have multiple view-models, we will be keeping our tutorial application rather small. Also be warned that this tutorial has a lot more to digest compared to the previous ones! By the end of this tutorial, we will have an application that displays two views, each with their own view-model. One of the views will be displaying some radio buttons, each representing a different color. When a radio button is clicked, we’ll publish a message that includes the appropriate color. The second view will listen for these messages and change the color of a Rectangle. We will do this in 4 steps: Adding another view to the application, implementing the IHandle<TMessage> interface, subscribe one of the view models to an event aggregator and finally publish events from the other view model.

Step 1: Adding Another View and View-Model

In order to demonstrate the event aggregator, we will need at least 2 view models in our application. We already have one (AppViewModel), so lets start by adding another one. Remember the naming convention described in the getting started tutorial? Add a new class called ColorViewModel, and a UserControl called ColorView. Lets also change the background of the ColorView so that we at least have something to see when we first add it to the application. In terms of the visual structure, we are going to get the existing AppView to contain the new ColorView. (Views do not need to be nested in order to use the event aggregator; a view-model can listen to messages being published from anywhere in the application.) To do this, AppViewModel will need a property of type ColorViewModel which we will set in the constructor like this:

public class AppViewModel : PropertyChangedBase
{
  public AppViewModel(ColorViewModel colorModel)
  {
    ColorModel = colorModel;
  }
 
  public ColorViewModel ColorModel { get; private set; }
}

In AppView.xaml, we will split the grid into 2 columns and display the ColorModelView in the first column. AppView.xaml will now look like this:

<Grid Width="300" Height="300" Background="LightBlue">
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="*" />
    <ColumnDefinition Width="*" />
  </Grid.ColumnDefinitions>
  <ContentControl Name="ColorModel" Margin="10" />
</Grid>

See what’s going to happen here? We have set the name of the ContentControl to be the same name as the property we just added to AppViewModel. From this, Caliburn Micro will kindly bind the Content property of the ContentControl to the ColorModel property for us. When we run this up later, Caliburn Micro will make sure that an instance of ColorView is displayed for the ColorViewModel.

If we were to run up the application now, we’d run into an exception saying that the default constructor of AppViewModel can’t be found. Hmm, that’s a good point: we have included a constructor on AppViewModel that requires a parameter – a ColorViewModel object. To resolve this, we’ll need to update our AppBootstrapper as seen below. (Make sure to include a reference to System.ComponentModel.Composition.dll to the application.)

using Caliburn.Micro;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
 
public class AppBootstrapper : Bootstrapper<AppViewModel>
{
  private CompositionContainer container;
 
  protected override void Configure()
  {
    container = new CompositionContainer(new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
 
    CompositionBatch batch = new CompositionBatch();
 
    batch.AddExportedValue<IWindowManager>(new WindowManager());
    batch.AddExportedValue<IEventAggregator>(new EventAggregator());
    batch.AddExportedValue(container);
 
    container.Compose(batch);
  }
 
  protected override object GetInstance(Type serviceType, string key)
  {
    string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
    var exports = container.GetExportedValues<object>(contract);
 
    if (exports.Count() > 0)
    {
      return exports.First();
    }
 
    throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
  }
}

This is similar to the bootstrappers used in the samples that come with the Caliburn Micro download. So as not to make this blog post too long, I won’t be diving into the details of what this code is doing (search for MEF or Managed Extensibility Framework if you need more details).

Next, we need to include an Export attribute on both our view-model classes. This is for the GetInstance method in the AppBootstrapper to work.

[Export(typeof(AppViewModel))]
public class AppViewModel : PropertyChangedBase
{
  ...
}
 
[Export(typeof(ColorViewModel))]
public class ColorViewModel
{
}

And finally, include the ImportingConstructor attribute on the AppViewModel constructor. This is to indicate that this constructor should be used since there is no default constructor. When Caliburn Micro creates the AppViewModel, it will also create an instance of the ColorViewModel to pass into the constructor for us.

[ImportingConstructor]
public AppViewModel(ColorViewModel colorModel)
{
  ColorModel = colorModel;
}

Now we can run the application and see that the ColorView is successfully being displayed within the AppView:

The application now displays 2 view models

Let’s add a Rectangle into the second column. This will be the Rectangle that changes color when messages are handled, so its color will be controlled by a property on the AppViewModel like this:

private SolidColorBrush _Color;
 
public SolidColorBrush Color
{
  get { return _Color; }
  set
  {
    _Color = value;
    NotifyOfPropertyChange(() => Color);
  }
}
<Rectangle Grid.Column="1" Width="100" Height="100" Fill="{Binding Color}" />

Step 2: Implementing the IHandle<TMessage> Interface

We are going to be publishing messages from the ColorViewModel to be picked up by the AppViewModel. To do this we are going to need to implement a class that holds the message information. such a class is usually very small and simple. It mainly needs to have some properties that hold any information that we want to send. Here is the message class that we are going to use:

public class ColorEvent
{
  public ColorEvent(SolidColorBrush color)
  {
    Color = color;
  }
 
  public SolidColorBrush Color { get; private set; }
}

In order for the AppViewModel to handle the appropriate events, it will need to implement the IHandle<TMessage> interface. In our case, we will be using ColorEvent as the generic type. The IHandle interface has a single method that we need to implement which is called Handle. In the Handle method of our AppViewModel, we will look at the SolidColorBrush sent in the ColorEvent and use it to set the Color property. This will in turn change the color of the Rectangle in the view.

public void Handle(ColorEvent message)
{
  Color = message.Color;
}

Step 3: Subscribe

Now we need to subscribe the AppViewModel to an event aggregator so that it can actually listen to published messages. We do this by adding another parameter to the constructor which will be an IEventAggregator. When the time comes to create the AppViewModel, Caliburn Micro will pass in the event aggregator that we set up in the bootstrapper. Now within the constructor, we simply call the Subscribe method like this:

[ImportingConstructor]
public AppViewModel(ColorViewModel colorModel, IEventAggregator events)
{
  ColorModel = colorModel;
 
  events.Subscribe(this);
}

Step 4: Publish

The ColorViewModel is also going to need the event aggregator so that it can publish messages. Add a constructor to the ColorViewModel which takes an IEventAggregator and stores it in a field. Remember to include the ImportingConstructor attribute:

private readonly IEventAggregator _events;
 
[ImportingConstructor]
public ColorViewModel(IEventAggregator events)
{
  _events = events;
}

Now we just need add the radio buttons to the ColorView, listen to their click event and then publish a message. You may remember from the second tutorial in this series about the quick way to listen to the click event. Simply set the name of the RadioButton to be the same as the method we want to be called. We could of course use event parameters rather than having an action method for every RadioButton, but I have done it like this so you can clearly see what’s going on:

<RadioButton Name="Red" Content="Red" Foreground="White"
             VerticalAlignment="Center" HorizontalAlignment="Center" />
<RadioButton Name="Green" Content="Green" Foreground="White"
             VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="1" />
<RadioButton Name="Blue" Content="Blue" Foreground="White"
             VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Row="2" />
public void Red()
{
  _events.Publish(new ColorEvent(new SolidColorBrush(Colors.Red)));
}
 
public void Green()
{
  _events.Publish(new ColorEvent(new SolidColorBrush(Colors.Green)));
}
 
public void Blue()
{
  _events.Publish(new ColorEvent(new SolidColorBrush(Colors.Blue)));
}

And that’s it. Run up the application now and click the radio buttons. See that the AppViewModel is successfully getting the messgaes from the ColorViewModel to change the color of the Rectangle.

Using the event aggregator

One thing to note is that I have been passing around SolidColorBrushes in the messages to set the color of the Rectangle. In general you would pass around more primitive values and then use a converter in the UI to interpret the value as a SolidColorBrush or the like.

The full Visual Studio 2010 project for this tutorial can be downloaded from here. I hope you find the event aggregator useful for orchestrating communication across the various view-models in your application. Check out the documention for more information including how to do polymorphic event subscriptions, custom publication marshaling and unsubscribe from the event aggregator.

See you next time.

Tagged as WPF

Caliburn Micro Part 3: More About Events and Parameters

So far in this Caliburn Micro tutorial series we have looked at how to get started with using Caliburn Micro in a WPF application, and how to link the view and the view-model together using data binding and events. In today’s tutorial, we will take a look at the more advanced ways of hooking up events and specifying event parameters. To experiment with these features, we will be further extending the application created in the previous two blog posts.

You may remember from the previous blog post that we hooked up events using the help of Caliburn Micro’s naming conventions. All we needed to do was set the name of a button in the view to be the same name as a method in the view-model. Then the Click event of the button will be automatically hooked up to call the appropriate method. Sometimes however you will need to explicitly hook up the events in order to provide event parameters or specify exactly which event you want to use. To do this, Caliburn Micro provides a long syntax and a short syntax. The advantage of the long syntax is its compatability with Microsoft Expression Blend. The short syntax is great if you’re not interested in using a designer and want to keep things short and sweet. Let’s start off with learning how to use the long syntax.

Long Syntax

The best way to begin is to modify the existing application to use the long syntax without changing the current behaviour. Open up AppView.xaml and include these 2 namespaces:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cal="http://www.caliburnproject.org"

Then, simply replace the repeat button with this:

<RepeatButton Content="Up" Margin="15" VerticalAlignment="Top">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Click">
      <cal:ActionMessage MethodName="IncrementCount" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
</RepeatButton>

Run the application now and you will see it looks and behaves just as it did before. What we have done here is used System.Windows.Interactivity triggers to hook up an event to a method. In the EventTrigger we can specify which event we want to listen to, and using the Caliburn Micro ActionMessage we can specify which method should be called. Using this approach you can include any number of event triggers to listen to other events on the same control. So you could listen to MouseEnter and MouseLeave to perform additional actions.

Next let’s look at event parameters. To demonstrate this, we will add another button that increments the count by 2. In AppViewModel.cs we need to modify the IncrementCount method to include an integer parameter. This parameter will be used to change the Count property. The IncrementCount method will now look like this:

public void IncrementCount(int delta)
{
  Count += delta;
}

Back in AppView.xaml, update the existing repeat button by adding a Caliburn Micro Parameter to the ActionMessage like this:

<cal:ActionMessage MethodName="IncrementCount">
  <cal:Parameter Value="1" />
</cal:ActionMessage>

Now when the button is pressed, it will pass a value of 1 as a parameter to the IncrementCount method. Add another button to the application with a parameter of 2 and then run up the application. Clicking on the different buttons will increment the count value according to their different parameters. The Value property of the Caliburn Micro Parameter is a dependency property, which means it also supports the usual WPF data binding. This allows you to use it in all sorts of different scenarios you come across.

Pro Tip: Because of the flexibity of using data binding to set the parameter value, it is possible to pass UI elements from the view into the view-model. You should try to avoid doing this as hard as you possibly can! UI elements in the view-model can fracture your MVVM archetecture and can cause maintenance issues in the future.

Short Syntax

Now to learn how to use the short syntax to do the same thing. To look into this, let’s start off with a fresh copy of the application that doesn’t have our recent long syntax experiments in it. Again we will start by modifying the application without changing its current behaviour. This time we only need to add the Caliburn Micro namespace, and replace the repeat button with this new one:

<RepeatButton Content="Up" Margin="15" VerticalAlignment="Top"
              cal:Message.Attach="[Event Click] = [Action IncrementCount]" />

All we have done this time is use one of Caliburn Micro’s attached properties (Message.Attach) to specify which event we are interested in, and which method to call.

Next we look at event parameters using this short syntax. Modify the IncrementCount method in same way we did for the long syntax. Including an event parameter using Message.Attach will look like this:

cal:Message.Attach="[Event Click] = [Action IncrementCount(1)]"

The event parameter is written in the brackets after the method name. I’m sure you can work out how to add a button that increments the value by 2.

The short syntax even supports a special form of data binding. To demonstrate this, let’s add a button that increments the count by the count value itself. In other words, a button that doubles the count value. You can remove the CanIncrementCount event guard mentioned in the previous blog post to make the value go higher than 100. The repeat button code would look something like this:

<RepeatButton Content="Double" Margin="15"
              cal:Message.Attach="[Event Click] = [Action IncrementCount(Count.Text)]" />

Here I have set the parameter to be Count.Text. This sets up a binding to the Text property of the TextBlock (named Count) which is displaying the current value. Notice that Caliburn Micro automatically converts string values that we want to pass into a method that takes a numerical value. Another shortcut that Caliburn Micro provides here is that it will automatically use an appropriate property on a user control if we do not specify a property. In the example above, we could just write the name of the TextBlock (“Count”) as the parameter and Caliburn Micro will bind to the Text property by default. Here is the what the application looks like so far:

Event Parameters

Automatically Finding Parameters

To finish off this tutorial I’ll mention that Caliburn Micro even has conventions for automatically picking up parameters when you don’t explicitly set them. If you do not explicitly specify a parameter, Caliburn Micro will look at the parameter name in the method signiture and try find any user control in the view that matches this name (ignoring the case). If a matching user control is found, an appropriate property on the control will be used to provide the parameter. For example, if the user control is a TextBlock, the Text property value will be used as the parameter. Again, Caliburn Micro can automatically convert strings to ints and so on if necessary.

To understand this convention more easily, lets try this out in the application. Add a slider to the application and call it “Delta”. Then add another button called “IncrementCount”. As explained in the previous blog post, the button is going to automatically call the IncrementCount method when it is clicked. This time however, the method has a parameter, but we havn’t specified what value the button should use. Notice that the slider we added is the same name as the parameter (delta). Thus Caliburn Micro will automatically use the Value property of the slider as the parameter to the method whenever the button is clicked. Here is all the code we needed to add to do this:

<UniformGrid Columns="2" VerticalAlignment="Bottom">
  <Slider Name="Delta" Minimum="0" Maximum="5" Margin="15" />
  <Button Name="IncrementCount" Content="Increment" Margin="15" />
</UniformGrid>

Parameter Convention

Extra for Experts

Here I’ve only covered some of the support that Caliburn Micro provides for hooking up events. Some other topics of intrest include:

  • Setting action targets
  • Special parameter values for data binding
  • Action bubbling
  • Action guards with parameters

You can find more information about hooking up events using Caliburn Micro from the online documentation. I recommend reading through the documentation so you can better choose which approaches to use for the various event scenarios in your application. You’ll find there is a lot of helpful support that Caliburn Micro provides, and a lot of convenient conventions that will take work off your shoulders. The Cheat Sheet is a useful page to bookmark. You’ll also find that the HelloExplicitAction, HelloParameters and BubblingAction samples are a good source of knowledge.

You can download the application from this tutorial used to demonstrate the long syntax from here, and the short syntax from here.

Next let’s take a look at the Event Aggregator.

Until next time, Happy Coding!

Tagged as WPF

Caliburn Micro Part 2: Data Binding and Events

In my previous blog post I showed you how to get started with using the Caliburn Micro Framework in a WPF application. Caliburn Micro helps us implement the application using the MVVM design pattern to get a clean separation between the view and the data model. In this blog post we’ll take a look at how Caliburn Micro assists us with data binding and events. We will be building on top of the application outlined in the previous blog post to add some simple user interaction and data display.

Data binding

We’ll start by getting the application to display a numerical value that is stored in the model. In the AppViewModel class created in the previous blog post, add a property called Count as seen in the code snippet below. The property value is stored in the _count field which we have given a default value of 50. You may recall from last time that we made the AppViewModel class extend the PropertyChangedBase provided by Caliburn Micro to preform property change notifications. Rather than implementing INotifyPropertyChanged in all of your models, you can simply call the NotifyOfPropertyChange method within the setter of your properties.

public class AppViewModel : PropertyChangedBase
{
  private int _count = 50;
 
  public int Count
  {
    get { return _count; }
    set
    {
      _count = value;
      NotifyOfPropertyChange(() => Count);
    }
  }
}

Next we modify the view to display this property value. In AppView.xaml, add a TextBox to the grid as follows:

<Grid MinWidth="300" MinHeight="300" Background="LightBlue">
  <TextBlock Name="Count" Margin="20" FontSize="150" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>

Now run up the application and see that the TextBlock is displaying the default value of the Count property.

A Caliburn Micro app displaying some data

Wait… what? I don’t remember setting any binding on the TextBlock to get the Count property, and yet the TextBlock is displaying the correct data!

Notice that I’ve set the name of the TextBlock to be the same as the property we want to bind to. This is a convenient short cut that Caliburn Micro provides. For elements that display data such as TextBlock or TextBox, setting their name to match a property on the data model will automatically hook it up for you.

Handling events

Next, let’s work on adding a button that increments the displayed value. The quick and dirty way of doing this is to hook up the Click event of a button to an event handler in the code behind. However, when using the MVVM pattern it’s usually best (but not absolutely necessary) to avoid using the code behind where you can. So let’s look at how to handle the events Caliburn Micro style. First, add a method to the AppViewModel for incrementing the Count property like this:

public void IncrementCount()
{
  Count++;
}

Now add a button to the grid in AppView.xaml as follows:

<Grid MinWidth="300" MinHeight="300" Background="LightBlue">
  <RepeatButton Name="IncrementCount" Content="Up" Margin="15" VerticalAlignment="Top" />
  <TextBlock Name="Count" FontSize="150" VerticalAlignment="Center" HorizontalAlignment="Center" />
</Grid>

Running the application now and clicking the button will increment the count value as advertised. Once again you’ll notice that we don’t need to do much work to hook the click event of the button to the IncrementCount method. For certain user interface controls such as buttons, you can simply set the name of the control to be the name of the method you want it to be hooked to. Caliburn Micro will hook the appropriate event of the user control to the specified method in the model. In the case of buttons, Caliburn Micro deals with the Click event. (You can also manually specifying which event to hook up to which method which we’ll look at next time).

Clicking the button increments the value

Event guards

When Caliburn Micro automatically hooks up the Click event to the IncrementCount method, it also looks for a method or property called CanIncrementCount. By adding a CanIncrementCount method or property, you can include additional logic that determines whether the event is allowed to be handled based on the current state of the model. Let’s do this now by adding the following property to the AppViewModel:

public bool CanIncrementCount
{
  get { return Count < 100; }
}

Since this logic is based on the value of the Count property, we also need to raise property change notification for the CanIncrementCount property whenever the Count value changes. This is done by adding this line of code on the Count property setter:

NotifyOfPropertyChange(() => CanIncrementCount);

Run up the application once more and increment the value to 100. Once the limit has been reached, the button will become disabled and prevent the user from further incrementing the value.

Button is disabled when count is 100

In this tutorial we have looked at a few ways that Caliburn Micro takes some of the work off our shoulders. Now you can more quickly use data binding, property change notifications, event handlers and event guards, while at the same time implementing a sturdy MVVM application that is easier to test and maintain.

You can download the full Visual Studio 2010 solution for this tutorial here. Use it to practice your new skills by adding a button to decrement the value.

In the next instalment of this blog series, we’ll take a look at more advanced scenarios of event handling with Caliburn Micro, specifically with passing event parameters. See you then.

Tagged as General, WPF

Caliburn Micro Part 1: Getting Started

A few days ago I blogged about our new Dashboard sample included in WPF Elements. The sample demonstrates how to create a sales dashboard using a few of our WPF controls like the Data Grid, Time Explorer and Charts. It was built with the help of the Caliburn Micro framework to give it a robust MVVM architecture. In this blog post I’ll walk you through a simple tutorial for getting started with using Caliburn Micro in a WPF application. This is just part 1 in a series I will be writing so I hope you’ll subscribe and follow along with me as we build up to a useful application.

Step 1: Getting Started

Caliburn Micro targets .NET framework 4.0, so you’ll need Visual Studio 2010 to create the application. Start by creating a new WPF Application and add a reference to Caliburn.Micro.dll and System.Windows.Interactivity.dll which come with the Caliburn Micro download. Since Caliburn Micro is going to take care of creating the window for you, delete MainWindow.xaml and remove the StartupUri attribute from App.xaml. App.xaml will now look like this:

<Application x:Class="CaliburnMicroApp.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Application.Resources>
 
  </Application.Resources>
</Application>

Step 2: The View Model

Caliburn Micro promotes a View-Model-First approach, so the next step is to add a class to represent the view model. Large applications can have many view models, each of which provides the logic for a different view. Below is a code example of an empty view model. Since this tutorial is focused on simply getting started with incorporating Caliburn Micro into a WPF application, we are not going to make the view model do anything for now.

using Caliburn.Micro;
 
namespace CaliburnMicroApp
{
  public class AppViewModel : PropertyChangedBase
  {
 
  }
}

The first thing to notice here is the name of the class. Caliburn Micro expects a particular naming convention so it can hook up the view model to the appropriate view. The class name of a view model should end with “ViewModel”. What you put in front of “ViewModel” is up to you. The other thing to notice here is that this class extends the PropertyChangedBase. This is provided by Caliburn Micro and makes it easy to raise property change notifications without needing to implement INotifyPropertyChanged in all your view models. Although this example view model doesn’t do anything, I’ve included the PropertyChangedBase as good practice. When adding properties to the view model, it will come in handy.

Step 3: The View

In order to display something in the window, you’ll need to create a view for the view model created in the previous step. This is as simple as adding a new UserControl to the project as seen below. I should point out again that Caliburn Micro expects a particular naming convention so it can hook up the view to the appropriate view model. View names should end with “View” and start with the same name you used for the view model. So for my example here, “AppView” is the view for rendering the “AppViewModel”. In the code below I have also set the width, height and background of the grid so that when you run this application you can see that it is working correctly.

<UserControl x:Class="CaliburnMicroApp.AppView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
  <Grid Width="300" Height="300" Background="LightBlue">
 
  </Grid>
</UserControl>

Step 4: The Bootstrapper

The bootstrapper is the mechanism used to incorporate Caliburn Micro into your application. It is also a place where you can configure the framework for the needs of your application. For the purposes of this tutorial, I have used a very simple bootstrapper implementation seen here:

using Caliburn.Micro;
 
namespace CaliburnMicroApp
{
  public class AppBootstrapper : Bootstrapper<AppViewModel>
  {
  }
}

Caliburn Micro has 2 different bootstrappers available. The one used above lets you set the generic type to be the view model that you want to be used at startup. The last step is to tell the application to use the bootstrapper. This is done by adding your bootstrapper to a resource dictionary in App.xaml. After doing this, App.xaml will now look something like this:

<Application x:Class="CaliburnMicroApp.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:local="clr-namespace:CaliburnMicroApp"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary>
          <local:AppBootstrapper x:Key="bootstrapper" />
        </ResourceDictionary>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

And that’s it

Stay tuned for Part 2 to see something with a little more substance

Now when you run up the application, you’ll see a small window with a light blue background. Your application is now primed and ready to take advantage of all the great support provided by Caliburn Micro. It’s easy to get help for using this framework due to its growing community. You can also look through the documentation on CodePlex.

You can grab the full Visual Studio 2010 solution I made for this tutorial here.

In the next part of this blog series we will look at adding some interactivity using events and data binding with the help of Caliburn Micro.

Until next time, Happy Coding!

Tagged as General, WPF

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top