Using Caliburn.Micro (version 2) for WPF development.

Part 1: A simple application illustrating some basic Caliburn.Micro concepts.

This item is based on the Caliburn.Micro documentation and Silverlight example here: http://caliburnmicro.codeplex.com/wikipage?title=Basic%20Configuration%2c%20Actions%20and%20Conventions
Apart from the additions of the DisplayName and TextBox colour items I make no claim to originality of the text – it is highly based on the original Caliburn.Micro “Soup to Nuts” documentation here: https://caliburnmicro.codeplex.com/documentation. I have merely adapted the code and text to illustrate the usage of Caliburn.Micro with WPF rather than Silverlight.

The Caliburn.Micro2.Hello Application

First create a WPF Application project in C# named Caliburn.Micro2.Hello

To add the latest (v2) Caliburn.Micro via NuGet:

Tools | NuGet Package Manager

If under the Package Manager Console install using the PM> Install-Package Caliburn.Micro command otherwise in the UI Manager search for Caliburn.Micro and install the Caliburn.Micro package only.

This will, actually, install both Caliburn.Micro and Caliburn.Micro.Core as well as a number of other required dependencies under the project References.

We are now ready to modify the structure and code for MVVM and Caliburn.Micro.

Add the following three folders to the project:

Models

ViewModels

Views

These serve as locations to keep the various items neatly separate! This example is simple enough not to need them (and the original tutorial does not use them). However, it is a good practise to start including these from the beginning.

Note: From now on I will use CM as an abbreviation for Caliburn.Micro.

Because CM uses its own Views you should delete the MainWindow.xaml file and remove the StartupUri item from App.xaml (StartupUri=”MainWindow.xaml”)

Change App.xaml.cs to:

using System.Windows;

namespace Caliburn.Micro2.Hello
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
        }
    }
}

Because CM prefers a View-Model-First approach, let’s start there. Create your first ViewModel in the ViewModels folder (Add C# class) and call it MainViewModel. Use the following code for the implementation:

using System.Windows;
using Caliburn.Micro;
namespace Caliburn.Micro2.Hello.ViewModels
{
    class MainViewModel : PropertyChangedBase
    {
        string name;
        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                NotifyOfPropertyChange(() => Name);
                NotifyOfPropertyChange(() => CanSayHello);
            }
        }

        public bool CanSayHello
        {
            get { return !string.IsNullOrWhiteSpace(Name); }
        }

        public void SayHello()
        {
            MessageBox.Show(string.Format("Hello {0}", Name)); //Don't do this in a real software project
        }
    }
}

Notice that the MainViewModel inherits from PropertyChangedBase. This is a CM base class that implements the infrastructure for property change notification and automatically performs UI thread marshalling.

There is a string property, Name, which uses CMs NotifyOfPropertyChange in set and a bool CanSayHello (read only) property which has a CM related method SayHello. SayHello just displays a MessageBox showing Name – this is for the example only – you wouldn’t want to “pollute” the view model with a UI “view” action in this way.

Note: The SayHallo method will become a button press action from a button named SayHello in the View. The boolean CanSayHello follows the CM convention and is used to enable/disable the button named “SayHello” in the View. These are based on standard naming conventions provided by CM.

We now need to create the bootstrapper that will configure the framework and tell it what to do. Create a new class named AppBootstrapper in the project “root” area. You can use this short item of code:

using System.Windows;
using Caliburn.Micro;
using Caliburn.Micro2.Hello.ViewModels; 

namespace Caliburn.Micro2.Hello
{
    class AppBootstrapper : BootstrapperBase
    {
        public AppBootstrapper()
        {
            Initialize();
        }
         protected override void OnStartup(object sender, StartupEventArgs e)
        {
            DisplayRootViewFor<MainViewModel>();
        }
    }
}

BootstrapperBase is a class provided by CM.

The Bootstrapper allows you to specify the type of “root view model” via the generic method (DisplayRootViewFor). The “root view model” is a ViewModel that Caliburn.Micro will instantiate and use to show your application.

Next, we need to place the AppBootstrapper somewhere where it will be run at startup. To do that, change your App.xaml to match this:

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

Note: Make sure you have removed the StartupUri (StartupUri=”MainWindow.xaml”) from the initial <Application ….> statement.

xmlns:local=”clr-namespace:Caliburn.Micro2.Hello” provides the local namespace

With our CM bootstrapper in the Application.Resources CM will do the rest of the work.

Run the application. You should see something like this:

CM2-Pt1-Image1

We have defined the ViewModel but not yet created the View to go with it.

To do this, create a new UserControl (WPF) in the Views folder with the name: MainView.xaml

<UserControl x:Class="Caliburn.Micro2.Hello.Views.MainView"
             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"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="300"  d:DesignWidth="300">
    <StackPanel>
        <TextBox x:Name="Name"></TextBox>
        <Button x:Name="SayHello" Content="Click Me"></Button>
    </StackPanel>
</UserControl>

The default control has been made a StackPanel – (you can use whichever WPF container you feel comfortable with).

Note that the x:Name items for the two controls match the names of the Property (Name) and Method (SayHello) in the MainViewModel code. It is this that CM uses to implement a convention based binding to certain actions.

Similarly, CM will look for a View with the same name as the ViewModel but without the Model part.

So for MainViewModel, CM looks for and uses a view named MainView.

Run the application and you should see something similar to:
CM2-Pt1-Image2
Type a name into the top text box and the button becomes enabled…
CM2-Pt1-Image3
Clicking the button throws up the message box:
CM2-Pt1-Image4
And we know that the system is working as we intended.

Adding a window title.

It would be quite good to give the App window a title. There’s a quick way to do this.

First, let’s make enough space to display it. In MainView.xaml set the MinWidth to 300.

d:DesignHeight="300" d:DesignWidth="300" MinWidth="300">

Then, in MainViewModel.cs add the IHaveDisplayName interface to the class declaration:

class MainViewModel : PropertyChangedBase, IHaveDisplayName

Implement the one and only property from that interface:

public string DisplayName { get; set; }

And create a simple no-parameter constructor which sets the DisplayName:

public MainViewModel()
{
     DisplayName = "My Hello App";
}

The complete class listing should now look like:

using System.Windows;
using Caliburn.Micro; 

namespace Caliburn.Micro2.Hello.ViewModels
{
    class MainViewModel : PropertyChangedBase, IHaveDisplayName
    {
        string name;
        public MainViewModel()
        {
            DisplayName = "My Hello App";
        }

        public string Name
        {
            get { return name; }
            set
            {
                name = value;
                NotifyOfPropertyChange(() => Name);
                NotifyOfPropertyChange(() => CanSayHello);
            }
        }
         public bool CanSayHello
        {
            get { return !string.IsNullOrWhiteSpace(Name); }
        }
         public void SayHello()
        {
            MessageBox.Show(string.Format("Hello {0}!", Name));
        }
         public string DisplayName { get; set; }
    }
}

Run the App and you should see:
CM2-Pt1-Image5
So you now have your App with a title.

Note: The IHaveDisplayName interface is also implemented in CMs IScreen and its Screen class implements IScreen. These are described later and on CM site here: https://caliburnmicro.codeplex.com/wikipage?title=Screens%2c%20Conductors%20and%20Composition

It’s a Bind

Let’s try another bit of binding between the View and ViewModel. This time it’ll be a specific item, that isn’t being automatically bound by CM.

Let us add the ability to set the colour of the TextBox from the ViewModel.

First we need to add the backing property and supporting code into MainViewModel:

private Brush _textBackgroundBrush; public Brush TextBackgroundBrush
{
    get { return _textBackgroundBrush; }
    set
    {
        _textBackgroundBrush = value;
        NotifyOfPropertyChange(() => TextBackgroundBrush);
    }
}

This creates a Brush property for the TextBox Background.

Note: This code structure is an excellent candidate for a ReSharper Live Template!

Now add calls in MainViewModel.cs to set the colour in the constructor:

public MainViewModel()
{
    DisplayName = "My Hello App";
    TextBackgroundBrush = new SolidColorBrush(Colors.BlanchedAlmond)
}

and the button click:

public void SayHello()
{
    TextBackgroundBrush = new SolidColorBrush(Colors.Chartreuse);
    MessageBox.Show(string.Format("Hello {0}!", Name));
}

Now we just need the binding in MainView.xaml:

<StackPanel>
    <TextBox x:Name="Name" 
             Background="{Binding TextBackgroundBrush, FallbackValue=LightBlue}">
    </TextBox>
    <Button x:Name="SayHello" Content="Click Me"></Button>
</StackPanel>

The FallbackValue displays in the absence of the Binding, as Binding is only made at Runtime the design colour of the TextBox will be LightBlue.

Run the application and see the colourful effect!

The complete solution is available as a zip download here (developed in Visual Studio 2013)

Advertisements