Caliburn Micro for Windows 8 Apps

I am a huge proponent of the MVVM (Model View ViewModel) pattern and try to use it whenever I can.  For the web its Knockout, for client devices its Caliburn.Micro, a framework that I have come to adore and be essential in my development.  Thus it follows naturally that I would want to use it in the Codemash Windows 8 application.  The only problem, right now, is the installation is not exactly straightforward and there is no NuGet package, though one is in the works I am told.

Installation

As I said, at the time of this writing, no NuGet package or download was available for the RT version.  I am told Rob (creator of Caliburn.Micro) is encountering a licensing problem for the package.  So until that is resolved, download the source code here and build it locally.  This will produce three DLLs that you will need to include in your project:

  • Caliburn.Micro.dll
  • Caliburn.Micro.Extensions.dll
  • Windows.Interactivity.dll

Make sure all three of these files are included in your solution and referenced in the appropriate projects.  These DLLs are compiled in such a way as to support both ARM and x86 compilation targets.

Configuration

I use CM a lot when developing Windows Phone 7 applications, and in that respect you have to write a Bootstrapper class to configure your injection container and apply any custom contentions.  Unfortunately, my injection container of choice, Ninject, either does not work in WinRT or I cant get it to compile locally.  One of the problems with using a new OS is many popular libs are in the experimental stage.  So in this case, eliminate all code in the App.xaml.cs file and perform the configuration there.  Rob was nice enough to provide the WinRTContainer class which can suffice for basic injection scenarios.  It does not support Property Injection, which is something I love in Ninject, but it is adequate. Here is the code comprising my configuration:

        protected override void Configure()
        {
            base.Configure();
            _container = new WinRTContainer(RootFrame);
            _container.RegisterWinRTServices();

            // repositories
            _container.RegisterSingleton(typeof(ISessionRepository), null,
                   typeof(JsonSessionRepository));
            _container.RegisterSingleton(typeof(ISpeakerRepository), null,
                   typeof(JsonSpeakerRepository));

            // custom support components
            _container.RegisterInstance(typeof(IAppService), null,
                   new CodemashApplicationService(RootFrame));
        }

        protected override object GetInstance(Type service, string key)
        {
            return _container.GetInstance(service, key);
        }

        protected override IEnumerable<object> GetAllInstances(Type service)
        {
            return _container.GetAllInstances(service);
        }

        protected override void BuildUp(object instance)
        {
            _container.BuildUp(instance);
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

To define the starting point, or the first ViewModel which should be loaded by Caliburn Micro, override the GetDefaultViewModel method, like below:

        protected override Type GetDefaultViewModel()
        {
            return typeof (SplashViewModel);
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Quick note: This method is only available if you are inheriting from CaliburnApplication.  Best way to do that is in the App.xaml file, below:

<caliburn:CaliburnApplication
    x:Class="Codemash.Client.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:caliburn="using:Caliburn.Micro">

  

</caliburn:CaliburnApplication>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

At this point your application will load your Default View Model (SplashViewModel in this case).  Remember the convention based approach.  SplashViewModel loads the SplashView.

Usage

As I started earlier, the WinRTContainer, provided by Caliburn.Micro, does not support Property Injection, so all injection is done through the constructor.  There are some standard ones that WinRTContainer provides automatically.  Perhaps most important of these is the INavigationService which allows you to navigate to different view models, thereby loading different views.  For example:

     public void ViewMap()
     {
         NavigationService.NavigateToViewModel();
     }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

In addition, you can pass parameters when you navigate, this allows you pass things like selected items to a detail view.  This part is a bit funky and differs from what we see in the WinPhone 7 implementation.  The recommended approach is define a Parameter type object to encapsulate the value, or values, you want to pass.  Example.

        public void SessionClick(ItemClickEventArgs args)
        {
            var listItem = (SessionView) args.ClickedItem;
            if (listItem.SessionId != 0)
            {
                var session = SessionRepository.Get(listItem.SessionId);
                NavigationService.NavigateToViewModel(
                   new SessionParameter(session));
            }
        }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

In order to receive this parameter, you must create a property on the corresponding view model (SessionDetailViewModel in this case) named Parameter.  During the navigation process, WinRTContainer will look for this property and inject the parameter value into it.  Example:

public class SessionsListViewModel : ViewModelBase
{
        // parameter
        public GroupingParameter Parameter { get; set; }

       // additional code
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Handing Interactive Events

One of my favorite aspects Caliburn.Micro is the configuration for handling events in custom ways.  Using the Windows.Interactivity library, you can augment the standard event handling mechanism within CM with the event arguments, like this:

        <ListView SelectionMode="None" IsItemClickEnabled="True"
                        ItemTemplate="{StaticResource SessionItemTemplate}">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="ItemClick">
                    <cal:ActionMessage MethodName="SessionClick">
                        <cal:Parameter Value="$eventArgs" />
                    </cal:ActionMessage>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </ListView>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Further information on special parameter passing can be found here.

Advertisements

2 thoughts on “Caliburn Micro for Windows 8 Apps

  1. Great write up. Thanks! We are working on a nuget package. I hope we can get it out in the next week or so. Also, regarding the parameter passing syntax for WinRT navigation, we have done some work recently to harmonize that with the WP7 version.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s