Code Smarter Not Harder #1: Caliburn.Micro

I often give presentations at Code Camps and such on how to easily assimilate new Microsoft technologies into your everyday coding tool belt. However, I have decided to start a series in order to demonstrate how to use Microsoft & non-Microsoft implemented solutions …some free some paid for… which can definitely help you out with certain projects.

So first up on deck is an awesome little framework known as Cliburn.Micro. This is a framework developed from the original Caliburn project by Rob Eisenberg ( blog | twitter ) . What I like about this framework is that it is really pretty lightweight … about 2K lines of code give or take and is built upon the simple premise that conventions can leveraged to make your programming life much easier. Such a simple thought, make a convention in your programming, write some a little code to take advantage of it, stick to it, and you don’t end up with carpal tunnel syndrome by the time you’re 35.

So first thing is first. You can get the Caliburn.Micro source code from CodePlex here or you can just pick up some template from the VS2010 Extension Manager Online Gallery as shown below.

image

So let’s fire up a new WPF project. Nothing fancy here we’re just picking a Caliburn Micro for WPF project template but you’ll notice there are also templates for Silverlight and Windows Phone as well. Also note that I am picking the bottom template which is off of the Gallery. The reason for this is because the other ones I got off of CodePlex actually contain the Caliburn.Micro source integrated into a project template. I just want the simple references to the dlls and that’s it.

image

If we look at the project. Everything is pretty simple. We have a Lib directory which contains the Caliburn.Micro and System.Windows.Interactivity dlls. Along with a ViewModels and Views folder which is pretty obvious what we have there.

Now we can go ahead and Build and run this little app and we will be greeted by a simple “Hello Caliburn.Micro” on the screen.

image

Not very coolio on the surface but let’s check what goes on beneath the covers. First up, let’s look at some of the code from the ShellViewModel.cs .

image

If you look closely…there is really nothing at all interesting about this VM. You’ve simply got a public property of Title that they are changing on firing up the VM to the hello message. BUT WAIT! Where is the code to tell what View it is that we are hooking up? Maybe it is wired up in the bootstrapper.cs file?

image

Nope?! Maybe it is tightly coupled somehow to the view?

image

Nope! So we’ve discovered our first really important convention and it is a simple naming convention for telling the VM or View depending upon which end of the spectrum you like to start…which is it’s default partner. So a [Insert name here]View.xaml  is paired up with a [Insert name here]ViewModel.cs . Pretty neat huh. Now, if I am following this convention and my team knows about the convention then they should know where to go for any given View or ViewModel to do anything in terms of troubleshooting or such.

Now let’s go back just really quickly to the VM again.

image

You’ll notice that the Model inherits from PropertyChangeBase and that provides us with a couple of different methods to ensure that our models properly implement INotify…. besides the RaisePropertyChangedEventImmediately which takes a string value you can also use the lambda based NotifyOfPropertyChange ..again pretty cool because I love wiring up a model base class for every project I do…yeah right.

Now let’s look again at the View for the shell.

image

What you should notice is that the TextBlock used to display our Title property doesn’t have an Binding on it! How can this be? Well, we’ve run into convention number 2 which is controls should have certain things bound by default… and if I name a control a certain name and there is a proper property exposed in the VM with the same name then it should be bound IF it is not already bound. The IF is important because what if we want to override the default. So if I change up the TextBlock to now read ….

    <Grid>
        <TextBlock x:Name="Title" Text="{Binding SomeotherTitle}"  />
    </Grid>

and change the VM to look like this …

image

Then when I run the app …it does as expected and doesn’t overwrite my custom binding…

image

That’s pretty cool but what about other controls? Well, it just so happens that the framework already accommodates a slew of WPF controls and you can actually bind up your own custom ones, like the Telerik control suite(that’s for another blog). So let’s make this a little better and change this interface so that you can type a message into a box and then hit a button and it will update the title on the screen. Nothing too terribly fancy on this first go around but just to show you some of the other capabilities.

So our View now is nothing artistically fancy….Title box on top…our Message in the middle and a button by the name of UpdateStuff on the bottom. 

image

So then we can change up our ViewModel to look thusly….

   1:  namespace CaliburnSample_WPF.ViewModels
   2:  {
   3:      using Caliburn.Micro;
   4:   
   5:      public class ShellViewModel : PropertyChangedBase
   6:      {
   7:          private string title;
   8:          public string Title
   9:          {
  10:              get { return title; }
  11:              set
  12:              {
  13:                  if (title != value)
  14:                  {
  15:                      title = value;
  16:                      RaisePropertyChangedEventImmediately("Title");
  17:   
  18:                  }
  19:              }
  20:          }
  21:   
  22:          private string _Message;
  23:          public string Message
  24:          {
  25:              get { return _Message; }
  26:              set
  27:              {
  28:                  _Message = value;
  29:                  NotifyOfPropertyChange(() => Message);
  30:              }
  31:          }
  32:   
  33:          public void UpdateStuff()
  34:          {
  35:              if (!string.IsNullOrEmpty(Message))
  36:              {
  37:                  Title = Message;
  38:                  Message = "";
  39:              }
  40:   
  41:          }
  42:   
  43:          public ShellViewModel()
  44:          {
  45:              Title = "Hello Caliburn.Micro";
  46:              Message = "";
  47:          }
  48:      }
  49:  }

So now if you look at this…you can see I have my Message property as expected but also that I have an UpdateStuff method that takes care of updating the title. So not only are my control properties hooked up via convention but also events for objects also can be wired up automatically to methods on my VM. Absolutely cool! So I can run my app now and everything should work .

image image

image

One last thing….if you notice by button “event” you’ll notice that I kind of clumsily check to make sure that the Message is actually set to something before I change it. Wouldn’t it be better to enable or disable the button based on the contents of the Message property? Wouldn’t it be even mo’ betta’ to have a convention that took care of that for us…I think you know where I am going with this one….

So now my VM code is as follows…

   1:  namespace CaliburnSample_WPF.ViewModels
   2:  {
   3:      using Caliburn.Micro;
   4:   
   5:      public class ShellViewModel : PropertyChangedBase
   6:      {
   7:          private string title;
   8:          public string Title
   9:          {
  10:              get { return title; }
  11:              set
  12:              {
  13:                  if (title != value)
  14:                  {
  15:                      title = value;
  16:                      RaisePropertyChangedEventImmediately("Title");
  17:   
  18:                  }
  19:              }
  20:          }
  21:   
  22:          private string _Message;
  23:          public string Message
  24:          {
  25:              get { return _Message; }
  26:              set
  27:              {
  28:                  _Message = value;
  29:                  NotifyOfPropertyChange(() => Message);
  30:                  NotifyOfPropertyChange(() => CanUpdateStuff);
  31:              }
  32:          }
  33:   
  34:          public void UpdateStuff()
  35:          {
  36:             
  37:                  Title = Message;
  38:                  Message = "";
  39:    
  40:   
  41:          }
  42:   
  43:          public bool CanUpdateStuff
  44:          {
  45:              get
  46:              {
  47:                  return !string.IsNullOrWhiteSpace(Message);
  48:              }
  49:          }
  50:   
  51:          public ShellViewModel()
  52:          {
  53:              Title = "Hello Caliburn.Micro";
  54:              Message = "";
  55:          }
  56:      }
  57:  }
 

You’ll notice that I added another property call CanUpdateStuff. Yet another convention is that a method can have a blocker just by putting Can in front of the name. So now all I am doing is checking to see if the Message property is null or whitespace and return true or false. All I have to do is to make sure that I notify the UI that the CanUpdateStuff property can change when the Message property is changed… Now my app looks like this…

image image

Exactly what I wanted the UI to do without having to touch the XAML at all. So you can imagine what this can do for you in terms of saving you a lot of grief and typing in a very large project. Plus, if you don’t like certain conventions then you are free to wire up your own…..the framework provides ways to add conventions and the code is only like 2K line long so it something that you can sit down with and figure out how everything works in an hour or two….

In future posts on Caliburn.Micro I will go into how to wire up these custom conventions for something like the Telerik control suite and I will go a little deeper into the conventions for properties such as visibility…

You can pick up the sample project code here

Sample Project Code : Caliburn.Micro WPF Project

Cheers!

AJ

Leave a Reply

Your email address will not be published. Required fields are marked *