Caliburn.Micro – Hello World

In this post we are going to create a simple WPF application using Caliburn.Micro.

I have created an empty Visual Studio 2010 solution for all the projects in this blog series. All of the code for this series will be available on my Github Learning Caliburn.Micro repository. But I suggest you follow along, and try and do it yourself.

  1. Start Visual Studio, and create a new WPF Application project called HelloWorld.
  2. Add references needed for Caliburn.Micro (Caliburn.Micro.dll and System.Windows.Interactivity.dll).
  3. Delete the MainWindow.xaml file.
  4. Create a directory named Views, and a directory named ViewModels.
  5. Create a user control in the views directory named MainView.
  6. Create a class in the ViewModels directory named MainViewModel.
  7. Create a class in the project root named HelloWorldBootStrapper.

Your project should now look like this:

That is the basic structure for a simple MVVM WPF application using Caliburn.Micro. There are other directories and files needed for more complex applications, but this is the basic starting point. So lets get to the code.

First, lets edit the HelloWorldBootStrapper class. Make the class public, if it is not already, then derive it from Bootstrapper<MainViewModel>. You will need using statements to Caliburn.Micro and HelloWorld.ViewModels. Here is what the class should look like:

Now we need to clean up App.xaml. Open that file for editing, and remove the StartUpi attribute from the Application tag. Next we need to add a namespace reference to the HelloWorld namespace, and add a resource dictionary that instantiates our bootstrapper class. Here is the final code:

The application class is the normal starting point of a WPF application, and what we have done is asked to create our bootstrapper when it starts, but nothing else. This is how we start Caliburn.Micro, which in turn starts running our code. We will revisit the bootstrapper is later posts, as it is where we can help tune our applications behavior, and add support of other common tools we may want to use like MEF or logging.

Up until now, we have been structuring our application to conform to the conventions that Caliburn.Micro uses as its default behavior. When working with Caliburn.Micro, you will learn that it adheres to a programming practice of Convention over Configuration. I won’t go into the specifics of that model, but strongly suggest watching Rob’s Mix 10 presentation Build Your Own MVVM Framework. It covers his thinking on the subject, and how Caliburn.Micro leverages that to minimize the amount of code needed to build an application.

Now lets actually build and run our application.{: .float_right }Exciting isn’t it? Ok. I kid. But I think it exhibits an important point. Where did the window come from? We deleted MainWindow.xaml. We did add a MainView user control, but we didn’t create that anywhere. So, I ask again, where did the window come from? The answer is, of-course, the framework (if it wasn’t, then this blog post would be really weird). So the real question is how did the framework know what to do. Let’s follow the flow. The application starts and creates the Application class (App.xaml). The XAML code for this class created an instance of our HelloWorldBootStrapper. The HelloWorldBootStrapper is derived from the Bootstrapper class (the first framework class we interact with), and this takes the type MainViewModel as the generic parameter. This is how the framework identifies the first view model to be created. The framework then removes the word ‘Model’ from MainViewModel to get the possible view name of MainView. It also removes the word ‘Model’ from the namespace HelloWorld.ViewModels to get the possible view namespace of HelloWorld.Views. The framework then checks for a view with the following type name ‘HelloWorld.Views.MainView’. If we look at our project, that is the type of our user control. The framework does some more magic to make this into a window and display it, but the important thing is that it found our view, bound that view to viewmodel, and then displayed it as the content of the window.

We are almost done now, so lets add some basic content. Open up the MainViewModel. Derive the class from PropertyChangedBase, which will require a using reference to Caliburn.Micro. Lets add some properties, and a command.

Add two private fields, _name and _helloString, both of type string.

private string _name;
private string _helloString;

Then add two properties Name and HelloString that implement the get and set for those two fields (Note: do not use automatic properties). When the value is set, call the method NotifyOfPropertyChance referencing the property. In the case of setting the Name, you also need to call NotifyOfPropertyChance on the property CanSayHello.

public string Name
{
  get { return _name; }
  set
  {
    _name = value;
    NotifyOfPropertyChange(() => Name);
    NotifyOfPropertyChange(() => CanSayHello);
  }
}

public string HelloString
{
  get { return _helloString; }
  private set
  {
    _helloString = value;
    NotifyOfPropertyChange(() => HelloString);
  }
}

Add a property called CanSayHello of type bool, that tests Name for null or whitespace and returns true if it is not.

return !string.IsNullOrWhiteSpace(Name);    

Now add the method called SayHello.

public void SayHello(string name)
{
  HelloString = string.Format("Hello {0}.", Name);
}

This example should look familiar if you have read Rob’s Caliburn.Micro Soup to Nuts Pt. 1 – Configuration, Actions and Conventions. I wanted to stick close to his Silver Light example to allow for comparison between WPF and Silver Light.

Your MainViewModel.cs file should look like this:

We are done with the model, so lets modify the view. Open up the MainView.xaml file. For simplicity I changed the Grid into a StackPanel. Now lets add some controls. Add a StackPanel with the Orientation attribute set to Horizontal. Then add a TextBlock with the Text attribute set to “Name: “, and a TextBox with the x:Name attribute set to “Name”. Close off the inner StackPanel. Add another TextBlock with the x:Name attribute set to “HelloString”. Then add a Button control with the x:Name attribute set to “SayHello” and the Content attribute set to “Click Me”. Finally, close off the StackPanel. Your MainView.xaml should look like this:

{:.float_right}

That’s it. We are done. Build and run the application. It should look like this. Nothing spectacular as it reflects the XAML code you typed in. However, pay close attention to the button. Notice it is disabled. Take a look at the XAML and you will see you that nowhere did you write any code to do that. In fact, even in the view-model code you didn’t write any code to disable the button. This is the real value add of the Caliburn.Micro framework. It made some assumptions based on convention, and it was able to auto-wire the view and the view-model in the binding process (see the bootstrapper above). The conventions applied here are designed to map the type of control, the name of the control (see x:Name), and either properties or methods in the view-model. Here are the bindings that the framework did for us:

View (XAML)ViewModel
ControlPropertyProperty/MethodData Type
TextBoxTextNamestring
TextBoxTextHelloStringstring
ButtonIsEnabledCanSayHelloboolean
ButtonClick EventSayHellomethod

These bindings were done for us by the framework using some basic conventions. In the case of the TextBox and TextBlock, the framework matched the x:Name with properties of the same name on the view-model. It used the data type of the properties to identify the right way to handle the binding (in other scenarios it will auto-invoke converters or data templates, for example). The button is a much more interesting situation. It matched the x:Name of the button with the name of a method on the view-model, and then created a command object (class implementing ICommand) to bind the two to each other. However, the framework employed one more convention. It looked for a property that was the same name as the method with ‘Can’ as a prefix. This is then used by the command object to determine if the button is enabled.

{:.float_right}

Now go back to the running window and type your name in to the TextBox. You will notice that the button is enabled right after the first character is entered. That is because the CanSayHello property on our view-model says that if Name property has any value that is not null, all whitespace, or empty, then it is valid. Click the button (you know you want to). The TextBlock is now populated. That happened because the view was notified of the change to the HelloString in the view-model. That change was made the same way the view knew about the Name changing, and that the CanSayHello property had changed. Look back into the MainViewModel. Notice the calls to the method NotifyOfPropertyChange? That method uses a bit of expression magic to identify the name of the property. I will go into that magic in another post. For now, just accept it works (since it does). Back to the method NotifyOfPropertyChange. It is inherited from the base class, PropertyChangedBase. This base class implements INotifyPropertyChanged which is what WPF (and Silver Light) uses to listen to changes in the value of a property. Calling that method just triggers that process which is part of the basic glue code of any WPF based application (with or without MVVM).

So that’s it. The basic MVVM application using Caliburn.Micro. In future blog posts we will build upon this basic application, integrating IoC containers and third-party component libraries, and increasing the complexity of the application.

As a reminder, all of the source code for this project is located in my Github Learning Caliburn.Micro repository for this project.

Related

comments powered by Disqus