Building Dashboards With WPF Elements Part 3: Getting Started

In the previous blog post, I showed you exactly what we will be building throughout this tutorial series. In this post we will start implementing the dashboard application and set it up using Caliburn Micro. This will mostly be a practical recap of most of the topics I covered in the Caliburn Micro tutorial series. If you are interested in the more in depth explanations of these topics, you can find our tutorials here:

Getting Started with Caliburn Micro
Data Binding and Events
More About Events and Parameters
The Event Aggregator
The Window Manager

Without further ado, let’s get into it.

Step 1: The project

Run up Visual Studio 2010 and create a new .NET 4.0 WPF Application. To get started, we need to add some assemblies. Create a new folder called Lib and add the Caliburn.Micro.dll, System.Windows.Interactivity.dll and the Microsoft.Windows.Shell.dll. Next add these as references to your project, and while we’re at it, add a reference to System.ComponentModel.Composition which will be needed later. You get the first 2 dlls when you download Caliburn Micro. The Microsoft.Windows.Shell.dll is what I use for creating custom window chrome which you can get by downloading the application at the end of this blog post. To finishing setting up, you can delete the MainWindow since Caliburn Micro will handle window creation for us. By deleting this, you’ll also need to remove the StartupUri from App.xaml

Step 2: The business model

As mentioned in the previous blog post, the main source of data for all the controls will be a list of orders. Create a new folder called Model, and add a class to represent an order. An order stores the customer name, the type of product that was purchased, the date and cost of the purchase as well as the organisation and country the customer comes from. How you store each of these values is up to you. The product type for example could either be a string, an enum or more likely, another model object that holds more information about the product. The Order class should expose these values with properties which we can bind to in the view.

Step 3: The view-model

Caliburn Micro promotes what is known as a view-model first approach, so next let’s add a class called DashboardViewModel to the project root. Extend the Caliburn Micro PropertyChangedBase class to aid in property change notifications. Since this dashboard sample is fairly simple with only 1 screen and no interchangable sections, we will only need a single view-model and view. For complex applications, Caliburn Micro has convenient features to make it easier to manage multiple models such as sending events between them. For now we will leave the model as an empty class. We’ll be adding properties and functions that use values from the business model in the next lesson.

Step 4: The view

Now we add a view for this view-model. This will be a UserControl which we’ll call DashboardView. Remember that Caliburn Micro uses a naming convention to find the view for a view-model. The name of the model should end with “ViewModel”, the name of the user control ends with “View” and they both start with the same name – in this case “Dashboard”. When following the MVVM pattern, I don’t like to write any code in the code behind. To avoid any temptation, go ahead and delete the DashboardView.xaml.cs file.

Step 5: The bootstrapper

In order to integrate Caliburn Micro into our application, we need a bootstrapper. Add a new DashboardBootstrapper class to the project and extend the Caliburn Micro Bootstrapper.

public class DashboardBootstrapper : Bootstrapper<DashboardViewModel>
{
}

The generic type of the bootstrapper tells Caliburn Micro which model will be used when the application starts up. This is all we need to do to the bootstrapper for now. We will come back to this soon to integrate a custom window manager. Next add an instance of your bootstrapper to App.xaml like this:

<ResourceDictionary>
  <ResourceDictionary.MergedDictionaries>
    <ResourceDictionary>
      <local:DashboardBootstrapper x:Key="bootstrapper" />
    </ResourceDictionary>
  </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

The application is now ready to use all that Caliburn Micro has to offer. Running the application now will display a tiny blank window.

Step 6: The window manager

It’s great that Caliburn Micro manages the creation of the window and hooks up the view to the appropriate view-model, but sometimes you want a bit of control over some of the properties of your windows. This is where the window manager comes in. Add a new class to the project called DashboardWindowManager and extend the Caliburn Micro WindowManager class. Here you can override the EnsureWindow method, call the base implementation and then set any proprties you need on the returned window. I’ve set SizeToContent to Manual and WindowState to Maximized. Next, go back to your bootstrapper and edit it like this:

public class DashboardBootstrapper : Bootstrapper<DashboardViewModel>
{
  private CompositionContainer container;
 
  protected override void Configure()
  {
    container = new CompositionContainer(new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
 
    CompositionBatch batch = new CompositionBatch();
 
    batch.AddExportedValue<IWindowManager>(new DashboardWindowManager());
    batch.AddExportedValue<IEventAggregator>(new EventAggregator());
    batch.AddExportedValue(container);
 
    container.Compose(batch);
  }
 
  protected override object GetInstance(Type serviceType, string key)
  {
    string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
    var exports = container.GetExportedValues<object>(contract);
 
    if (exports.Count() > 0)
    {
      return exports.First();
    }
 
    throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
  }
 
  protected override IEnumerable<object> GetAllInstances(Type serviceType)
  {
    return container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
  }
 
  protected override void BuildUp(object instance)
  {
    container.SatisfyImportsOnce(instance);
  }
}

Most of this is seen in the samples that come with the Caliburn Micro download. The important line here is where we add an instance of our custom dashboard window manager in the Configure method. Don’t forget that these changes to the bootstrapper require us to add an Export attribute to the DashboardViewModel class like this:

[Export(typeof(DashboardViewModel))]

Back in the DashboardWindowManager, some other useful things we can do is set the application icon and apply custom window chrome. These are probably some of the last things you would add in an application, but on the topic of the window manager, now is a good time to mention them. You can add an icon file to your project and then use BitmapFrame.Create to set the Icon property of the window. For the custom window chrome, I used the Microsoft.Windows.Shell dll. In App.xaml I created the window style which I then find in the window manager and apply to the window. You’ll also need to attach some event handlers as seen in the code below. Here is what the final window manager looks like:

public class DashboardWindowManager : WindowManager
{
  private Window _window;
 
  protected override Window EnsureWindow(object model, object view, bool isDialog)
  {
    _window = base.EnsureWindow(model, view, isDialog);
    _window.SizeToContent = SizeToContent.Manual;
    _window.WindowState = WindowState.Maximized;
    _window.Style = _window.FindResource("CustomWindowStyle") as Style;
 
    Uri icon = new Uri("../../WpfElements.ico", UriKind.Relative);
    _window.Icon = BitmapFrame.Create(icon);
 
    _window.CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, OnSystemCommandCloseWindow));
    _window.CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, OnSystemCommandMinimizeWindow));
    _window.CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, OnSystemCommandMaximizeWindow));
    _window.CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, OnSystemCommandRestoreWindow));
 
    return _window;
  }
 
  private void OnSystemCommandCloseWindow(object sender, ExecutedRoutedEventArgs e)
  {
    SystemCommands.CloseWindow(_window);
  }
 
  private void OnSystemCommandMinimizeWindow(object sender, ExecutedRoutedEventArgs e)
  {
    SystemCommands.MinimizeWindow(_window);
  }
 
  private void OnSystemCommandMaximizeWindow(object sender, ExecutedRoutedEventArgs e)
  {
    SystemCommands.MaximizeWindow(_window);
  }
 
  private void OnSystemCommandRestoreWindow(object sender, ExecutedRoutedEventArgs e)
  {
    SystemCommands.RestoreWindow(_window);
  }
}

Step 7: The layout

Finally, in DashboardView.xaml, we can rough out the layout of the dashboard in preparation for adding the controls in the next lesson. Throughout this tutorial, I add application specific resources to App.xaml. In large applications, you would add such resources to one or more resource dictionaries. For the purposes of this blog post, I have placed a colored rectangle in place of each control that we will be adding later. Here is where the application is at now:

Dashboard Layout

You can download the Visual Studio 2010 solution of where we are at so far from here.

Join me next time as we add properties to the view-model and hook them up to controls in the view. We will also be setting up events from the view back to the view-model which will give us a fully functional dashboard application.

Tagged as WPF Elements

One Response to “Building Dashboards With WPF Elements Part 3: Getting Started”

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top