Introduction to Caliburn.Micro on Windows Phone 8
I thought I'd try something different with my latest home software project; after writing a "ViewModelBase" INotifyPropertyChanged implementation for the millionth time (I really should start creating some shared libraries) I decided to have a look at one of many MVVM libraries out there and stumbled across Caliburn.Micro. Here are some newbie notes on my experiences.
Windows Phone 8 Support
I'm writing a Windows Phone 8 application, so fell at the first hurdle after installing the package through nuget and following the tutorial here. This doesn't work under WinRT and so my attempts at using the generic form of Bootstrapper were met with
The non-generic type 'Caliburn.Micro.Bootstrapper' cannot be used with type arguments.
instead you need to derive from PhoneBootstrapper to end up with something looking like this:
public class AppBootstrapper : PhoneBootstrapper { PhoneContainer container; protected override void Configure() { container = new PhoneContainer(); container.RegisterPhoneServices(RootFrame); container.PerRequest<MainPageViewModel>(); AddCustomConventions(); } static void AddCustomConventions() { } protected override IEnumerable <object> GetAllInstances(Type service) { return container.GetAllInstances(service); } protected override void BuildUp(object instance) { container.BuildUp(instance); } }
Which you can then use as a standard template bootstrapper.
Views and ViewModels
Caliburn.Micro applies a View-Model First approach wiring up its Views and ViewModels though a naming convention. If you put all of your Views in a folder called \Views and all of your ViewModels in a folder called \ViewModels under your project root you can then register your ViewModel with the container in your bootstrapper (as I have done with MainPageViewModel in the code above) and it will automatically look for a view called MainPage.xaml in the \Views folder. I quite like this as it matches my usual folder structure for an MVVM project and takes away some of the leg work.
The container can either register services PerRequest, where a new instance is created every time it is requested, or Singleton which will return the same single instance to each request.
Caliburn.Micro will automatically set the DataContext of the View to the appropriate ViewModel. It provides a base class PropertyChangedBase to derive your ViewModel classes from which handles the usual INotifyPropertyChanged stuff and provides a NotifyOfPropertyChange method which can either take a property name String or an Expression to represent the property like this:
private string _myString; public string MyString { get { return _myString; } set { _myString = value; NotifyOfPropertyChange(() => MyString); } }
The advantage of using the expression is that if you rename the property and forget to update the name in the expression, this will result in a compilation error, rather than a run-time binding error if you used a string. Visual Studio's Rename function will also know to change this.
DataBinding
With your Views and ViewModels wired up magically as above, Caliburn.Micro then provides you with some nice shorthand for DataBinding. It's important to point out that all of the usual {Binding} syntax works as well.
If we had a TextBlock in MainPage.xaml and wanted it to show the text contained in the MyString property on MainPageViewModel.cs we can simply do this:
<TextBlock x:Name="MyString"/>
Which is pretty handy, although seems to run out of steam as soon as you want any more complicated binding. If I wanted to not only bind the text to MyString but the foreground colour to another property on my ViewModel you can fall back to the standard Binding markup extension and use both like this:
<TextBlock x:Name="MyString" Foreground="{Binding MyColour}"/>
Commands
Commands have also been simplified by Caliburn.Micro, let's dive straight in with an example. A Button on my MainPage.xaml defined like this:
<Button x:Name="ShowMyString"/>
Will call the method ShowMyString on the MainPageViewModel.cs on click.
This is again quite powerful, but worth bearing in mind that by default will wire up every event on the element to that method. If you want further control, you can use the following syntax to explicitly wire up just the Click event:
<Button cal:Message.Attach="[Event Click] = [Action ShowMyString]"/>
You can pass parameters into the method like this:
<Button cal:Message.Attach="[Event Click] = [Action ShowMyString($this)]"/>
Which will pass a reference to the UI element the action is attached to. Other parameters are as follows:
- $eventArgs - The EventArgs or input parameter for your action.
- $dataContext - The DataContext of the element that the action is attached to.
- $source - The FrameworkElement that triggered this action.
- $view - The View that is associated with this ViewModel.
- $eventArgs - The EventArgs or input parameter for your action.
To hook into the CanExecute part of ICommand you simply create a method called CanShowMyString() in your ViewModel (just prefix the execute method name with "Can"). To update this and ensure that the CanExecute is re-evaluated make sure you call:
NotifyOfPropertyChange(() => CanShowMyString);
where appropriate.
And that concludes my first quick look at Caliburn.Micro. There are lots more to it than I've covered here but you can see how the "magic" can make for some very powerful yet clear and easy to maintain (once you know what its doing) MVVM abstraction. I'll keep playing and post a follow-up article when I get further along with my app.