Building Dashboards With WPF Elements Part 4: Hooking up the view and model

In the previous blog post of this tutorial series, we started implementing the dashboard application. It’s all hooked up to Caliburn Micro with a view-model ready for the logic, and a view that is ready to host all the controls. By the end of this blog post, we’ll have a fully functional dashboard with charts and a data grid that display data based on a time range selected by the user.

Adding WPF Elements

The dashboard is going to use various controls from our WPF Elements control suite. So to start off, make sure to have your copy of WPF Elements ready. If you haven’t got it yet, you can download the free trial from here. In the dashboard project, add a reference to the Mindscape.WpfElements.dll. You can reference this directly from the folder it was installed to, or if you copy it to a different folder, make sure to copy the .lic files with it. Next add a licenses.licx file to the Properties folder. You can copy this file from any of the samples that are installed with WPF Elements, or get it from the project attached to the end of this blog post.

Adding properties to the model

Next we’ll add properties to the view-model that the controls in the view will bind to. One of the properties we will need is a double value that stores the total product revenue across the selected time range. Add a double field to the DashboardViewModel called _totalRevenue. Then add the double property like this:

private double _totalRevenue;
 
public double TotalRevenue
{
  get { return _totalRevenue; }
  private set
  {
    _totalRevenue = value;
    NotifyOfPropertyChange(() => TotalRevenue);
  }
}

Here you can see the getter simply returns the field, and the setter is using the Caliburn Micro NotifyOfPropertyChanged method to raise property changed notifications. This means as we change the property values in the view model, the controls that we bind to the properties later will update their visuals. Some other simple properties we will need is the StartdDate, EndDate and a couple of properties for displaying the values in the radial gauges. The StartDate and EndDate need to have a public setter because the time explorer will use a two way binding. This lets the model know the date range after the user has changed it. As for the charts, each data series will need to bind to a property that gets a collection of data points. The time explorer displays a single line series, and the other 2 charts display 3 series each – one for each product type. Then last of all is a collection of orders within the current time range which gets displayed by the data grid. When the user changes the range on the time explorer, the entire items source of the charts and data grid are going to be updated. For this application I have made these properties get an IList. In other applications, you may want to use observable collections if you are making smaller updates to the items sources such as adding data points over time.

Generating the data

Now to give these properties some values. For now the source of the dashboard data will be a randomised collection of orders. In another lesson, we will be replacing this with a database. In the view-model, we will need to store the entire list of orders which will be used to calculate values whenever the time range changes. In the demo attached to this blog post, you will see I have put this in a field called _allOrders. In the view-model constructor I am setting this field using the RandomDataBuilder helper class I added to the Model folder.

The chart displayed in the time explorer is always going to display the same data. So after randomly generating the data, we can use a loop to iterate through all the orders and aggregate the cost data into 2 hour time slots. The resulting collection is used to set the appropriate property that will be displayed by the time explorer chart.

All the other properties will be updated as the user changes the time range. We will have a method that updates these properties as the date range changes. I have called this method UpdateData. When the application starts up, we set a default date range and call this update method. The method iterates though all the orders within the current date range to aggregate each of the 3 types of product sales into 2 hour time slots. The data is also grouped with the same country to calculate the top 10 country statistics and finally we sum up the total revenue. These values are then used to set the appropriate properties on the view-model.

Adding controls to the view

Now that the view-model has some data to display, lets move into DashboardView.xaml to add the controls. Firstly, make sure to include the Mindscape WPF Elements namespace. At the top we have the time explorer with the revenue summary chart. The time explorer has RangeStartTime and RangeEndTime properties which control the inner view port slider. These properties will bind to the StartTime and EndTime in our view-model. Here we use a TwoWay binding so we can get these values in the view-model when the user changes the date range. When the application is run up, Caliburn micro sets the data context of the view to be the view-model, so the bindings are very straight forward.

<ms:TimeExplorer Grid.Row="1" Grid.ColumnSpan="2" RangeStartTime="{Binding StartDate, Mode=TwoWay}" RangeEndTime="{Binding EndDate, Mode=TwoWay}">
  <ms:Chart>
    <ms:AreaSeries ItemsSource="{Binding RevenueSummary}" />
  </ms:Chart>
</ms:TimeExplorer>

Look through the application attached to this blog post to see how I’ve setup the other controls. For the chart series and the data grid, we simply bind the ItemsSource to the appropriate collection properties. In the bottom right corner of the dashboard, the Text of the labels and the Value of the gauges bind to their appropriate properties. And that’s it for binding the view to the view-model. With the model managing the data, and the view binding to the model, running the application now will get you something like this:

Dashboard

Note that since we are creating roughly 1 million randomized product orders, it will take several seconds to load up the application. We will be considering this loading performance in the next lesson. For now, you can edit the RandomDataBuilder class to generate less data if you like.

Hooking up events

Now to make the dashboard interactive. The main interaction comes from the time explorer which is used to change the date range. Remember that we are binding the start and end values of the time explorer viewport to properties in the model, so you may think that the setter of these properties is a good place to call the UpdateData method. However, these properties constantly change as the user drags the viewport thumbs which would abuse the heavy UpdateData method and kill the performance. Instead, the time explorer provides an event that gets raised when the user has fully completed a range changed operation. What we need to do is listen to this event being raised, and then call the UpdateData method. I’ve discussed how to do this in a previous blog post. We can use the short syntax by adding this to the TimeExplorer tag:

cal:Message.Attach="[Event TimeRangeChanged] = [Action UpdateData()]"

You can’t get more simple than that. Running the sample now will have all the controls updating their displayed data whenever the date range is changed.

Slick Dashboard Time Explorer

At the very bottom right corner of the dashboard is a button that takes you to our website. When the button is clicked, a method in the view-model will open the web page. But if you look at the xaml, it doesn’t appear that the button has any event listener code like what we just did with the time explorer.

<Button Name="VisitWebsite" Grid.Row="1" Style="{StaticResource LinkButtonStyle}" HorizontalAlignment="Left" ToolTip="Visit the Mindscape website">
  <TextBlock Text="Built with Mindscape WPF Elements" FontSize="16" FontFamily="Segoe UI" Foreground="DarkGray" />
</Button>

Instead, I am using a Caliburn Micro naming convention that causes the method to be called when the button is clicked due to the button having the same name as the method (VisitWebsite).

Filtering the data
The last interactive part of the dashboard that we need to hook up to the model are the filter buttons at the top next to the title. The idea is that when one of these buttons is pressed, the other buttons go semi-transparent and some of the chart series disappear, leaving behind the data that the user is interested in. This suggests that we need to add 3 boolean properties in the view-model which specify whether the data for a particular product type should be displayed. Each of the buttons can bind to these properties and use a converter to change the opacity.

You can hide the data of our chart controls by setting the visibility of a chart series. So here we can easily bind the visibility of each chart series to these properties and use a BooleanToVisibilityConverter.

<ms:StackedAreaSeries ItemsSource="{Binding Product1Data}" SeriesBrush="{StaticResource ProductColor1}" Title="{x:Static local:DashboardViewModel.ProductName1}"
                      Visibility="{Binding IsProduct1Displayed, Converter={StaticResource bvc}}" />

Now we need to listen to the buttons being clicked to update the filtering in the model. This will be done in the same way as we listened to the time explorer. All three buttons will call the same method, so this time we will include an event parameter so the method knows which product type is being filtered. I have simply used the integers 1,2 and 3 to specify the different product types. With Caliburn Micro you can include event parameters like this:

cal:Message.Attach="[Event Click] = [Action Filter(1)]"

In the Filter method, you include the logic that changes the values of the boolean properties we just added. Then we filter the collection displayed by the data grid, and change the total revenue value to only consider the filtered product type. All the code for this can be seen in the demo attached to this blog.

You can download the Visual Studio 2010 solution of the current state of the dashboard sample from here. To run the sample, make sure to include a reference to your copy of the Mindscape.WpfElements.dll. If you don’t have WPF Elements, you can download the free trial from here.

Next time I’ll show you how to add finishing touches to the view such as custom styles and templates. See you then!

Tagged as WPF Elements

3 Responses to “Building Dashboards With WPF Elements Part 4: Hooking up the view and model”

  • […] Building Dashboards With WPF Elements Part 4: Hooking up the view and model (Jason Fauchelle) […]

  • What happened to the database part and maybe WCF

  • Hello Gareth

    We’ve been busy these days with all the Windows 8 activity. We’ll try make some time to finish off this series.

  • Leave a Reply

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top