Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

January 29, 2011

Charting in a Silverlight Application using MVVM

Filed under: C#,Silverlight,VB.NET @ 6:22 pm

Data visualization is often a requirement in today’s line of business (LOB) applications. Plus charts are just more fun to look at than data grids. Silverlight provides many different types of charts you can use to display the data in your LOB application.

NOTE: This post is part of a series that starts with this prior post. The example in this post uses the application that is built as part of that series.

As discussed in this prior post, using Model-View-View/Model (MVVM) comes with many benefits. One of those benefits is an easy way to define design-time data, which is a real plus when trying to design and style your charts.

Step 1: Drag and drop a Chart from the toolbox onto the desired page.

If you are working through the set of blog posts, add a new Page called StudentChart and add the chart to that page. (Be sure to add a link from the main page to this page using the technique from this prior post.) Otherwise, add a Chart to any page.

The chart immediately appears in the designer like this:

image

Two things to notice about the chart:

1) The chart has data. The designer automatically hard-codes data points into the xaml so you can see the chart. You will have to delete these points to hook up your actual data. But as soon as you delete the points, you no longer have design time data. More on this issue later in this post.

2) The legend may appear as a blank rectangle on the right instead of an actual legend. This will occur if you followed this prior post and defined a custom theme for your application. The custom theme has a bug which prevents it from correctly displaying the legend. More on this in a different post.

Step 2: Build/generate the business objects that contain the data for the chart. This step requires a fundamental decision on how you will get data into your application. The primary choices are:

  • Build an entity class in the Silverlight application that is populated from a WCF service.
  • Generate an entity class in the Silverlight application using WCF RIA services and Entity Framework.
  • Generate an entity class in the Silverlight application using WCF RIA services and your business objects (sometimes called Plain Old CLR Objects or POCOs). See this prior post for more information on using WCF RIA with your business objects.

The details of these approaches is beyond the scope of this post, so this example uses an entity class in the Silverlight application that is populated from code to keep things simple.

If you are working through the set of blog posts, you already built the model in this prior post. If not, the model code is shown here:

In C#:

using System;

namespace InStepSM.SL.Models
{
    public class Student
    {
        public String StudentName { get; set; }
        public int StudentId { get; set; }
        public decimal Project1Score { get; set; }
        public decimal Project2Score { get; set; }
        public decimal Project3Score { get; set; }
    }
}

In VB:

Namespace Models
    Public Class Student
        Public Property StudentName As String
        Public Property StudentId As Integer
        Public Property Project1Score As Decimal
        Public Property Project2Score As Decimal
        Public Property Project3Score As Decimal
    End Class
End Namespace

NOTE: The VB code Namespace includes only "Models" because VB automatically prepends the application namespace to any defined namespace in the application. The result is InStepSM.SL.Models in this case, just like the C# code.

This class includes a student’s name, Id, and three scores.

Step 3: Build the View/Model class, exposing properties to bind to the View.

Add a StudentChartViewModel class to your application. Be sure to implement INotifyPropertyChanged. This ensures that the UI is notified when properties of the View/Model are changed.

NOTE: INotifyPropertyChanged requires the System.ComponentModel namespace.

Add a property that contains the list of students. The View will bind to this property. You can define the property to be a List, but if you instead use an ObservableCollection, changes to the list will be reflected in the user interface.

NOTE: ObservableCollection requires the System.Collections.ObjectModel namespace.

Also add a property that contains the score titles. These will appear in the legend for each score.

In C#:

using System.Collections.ObjectModel;
using System.ComponentModel;
using InStepSM.SL.Models;

namespace InStepSM.SL.ViewModels
{
    public class StudentChartViewModel: INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private ObservableCollection<Student> _studentList;
        public ObservableCollection<Student> StudentList
        {
            get
            {
                return _studentList;
            }
            set
            {
                if (_studentList != value)
                {
                    _studentList = value;
                    OnPropertyChanged("StudentList");
                }
            }
        }

  private List<string> _titleList;
  public List<string> TitleList
  {
      get
      {
        return _titleList;
      }
      set
      {
          if (_titleList != value)
          {
            _titleList = value;
            OnPropertyChanged("TitleList");
          }
      }
  }

        public StudentChartViewModel()
        {
            PopulateStudents();
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)        
                PropertyChanged(this,
                    new PropertyChangedEventArgs(propertyName));
        }

        public void PopulateStudents()
        {
            var itemList = new ObservableCollection<Student>()
                    {new Student(){StudentName="Frodo Baggins",
                                    Project1Score = 89M,
                                    Project2Score=93M,
                                    Project3Score=88M},
                    new Student(){StudentName="Rosie Cotton",
                                    Project1Score = 97M,
                                    Project2Score=93M,
                                    Project3Score=94M},
                    new Student(){StudentName="Samwise Gamgee",
                                    Project1Score = 83M,
                                    Project2Score=90M,
                                    Project3Score=85M},
                    new Student(){StudentName="Peregrin Took",
                                    Project1Score = 69M,
                                    Project2Score=72M,
                                    Project3Score=75M}};
            StudentList = itemList; 

       List<string> itemNameList = new List<string>
                       {"PreTest", "Chp 1", "Test"};
       TitleList = itemNameList;
   }

    }
}

In VB:

Imports System.Collections.ObjectModel
Imports System.ComponentModel
Imports InStepSM.SL.Models

Namespace ViewModels
    Public Class StudentChartViewModel
        Implements INotifyPropertyChanged

        Public Event PropertyChanged(ByVal sender As Object,  
         ByVal e As System.ComponentModel.PropertyChangedEventArgs)  _
Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged

        Private _studentList As ObservableCollection(Of Student)
        Public Property StudentList As ObservableCollection(Of Student)
            Get
                Return _studentList
            End Get
            Set(ByVal value As ObservableCollection(Of Student))
                If _studentList IsNot value Then
                    _studentList = value
                    OnPropertyChanged("StudentList")
                End If
            End Set
        End Property

        Private _titleList As List(Of String)
        Public Property TitleList As List(Of String)
            Get
                Return _titleList
            End Get
            Set(ByVal value As List(Of String))
                If _titleList IsNot value Then
                    _titleList= value
                    OnPropertyChanged("TitleList")
                End If
            End Set
        End Property

        Public Sub New()
            PopulateStudents()
        End Sub

        Protected Sub OnPropertyChanged(ByVal propertyName As String)
            If Not String.IsNullOrEmpty(propertyName) Then
                RaiseEvent PropertyChanged(Me,
                          New PropertyChangedEventArgs(propertyName))
            End If
        End Sub

        Public Sub PopulateStudents()
            Dim itemList = New ObservableCollection(Of Student)() From
                 {New Student() With {.StudentName = "Frodo Baggins",
                                    .Project1Score = 89D,
                                    .Project2Score = 93D,
                                    .Project3Score = 88D},
                 New Student() With {.StudentName = "Rosie Cotton",
                                    .Project1Score = 97D,
                                    .Project2Score = 93D,
                                    .Project3Score = 94D},
                 New Student() With {.StudentName = "Samwise Gamgee",
                                    .Project1Score = 83D,
                                    .Project2Score = 90D,
                                    .Project3Score = 85D},
                 New Student() With {.StudentName = "Peregrin Took",
                                    .Project1Score = 69D,
                                    .Project2Score = 72D,
                                    .Project3Score = 75D}}
            StudentList = itemList

            Dim itemNameList = New List(Of String)(
                New String() {"PreTest", "Chp 1", "Test" })
            TitleList = itemNameList

        End Sub

    End Class
End Namespace

Step 4: Hook the View to the View/Model. This example defines the instance of the View/Model in the xaml so that the xaml has access to sample data. The code is shown in red below.

<navigation:Page x:Class="InStepSM.SL.Views.StudentChart"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="
http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc=
      "
http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:navigation=
       "clr-namespace:System.Windows.Controls;assembly=
       System.Windows.Controls.Navigation"
    xmlns:vms ="clr-namespace:InStepSM.SL.ViewModels"
    d:DesignWidth="640" d:DesignHeight="480"
    xmlns:toolkit=
  "
http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
    Title="StudentChart Page">

    <!– Reference the View/Model –>
    <navigation:Page.DataContext>
        <vms:StudentChartViewModel/>
    </navigation:Page.DataContext>

</navigation:Page>

Step 5: Bind the Chart to View/Model properties.

Set the chart’s ItemSource to the StudentList property. Then set the IndependentValuePath to the student’s name and the DependentValuePath to the desired score.

To set the title of each bar in the chart in the legend, bind the Title property to the desired TitleList element. (The ability to bind to an element in a List or Array was added in Silverlight 3.)

Notice that as soon as you set the binding properties, you will again see sample data in the chart in design mode. This is the data from the View/Model.

To add all three scores, add multiple columnSeries elements to the xaml. The resulting xaml looks like this:

<navigation:Page x:Class="InStepSM.SL.Views.StudentChart"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="
http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="
http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc=
        "
http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=
         System.Windows.Controls.Navigation"
    xmlns:vms ="clr-namespace:InStepSM.SL.ViewModels"
    d:DesignWidth="640" d:DesignHeight="480"
    xmlns:toolkit=       "
http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit"
    Title="StudentChart Page">

    <!– Reference the View/Model –>
    <navigation:Page.DataContext>
        <vms:StudentChartViewModel/>
    </navigation:Page.DataContext>
   
    <Grid x:Name="LayoutRoot">
       
<toolkit:Chart Name="chart1"
               Title="Scores"
               VerticalAlignment="Stretch"
               HorizontalAlignment="Stretch">
        <toolkit:ColumnSeries DependentValuePath="Project1Score"
                    IndependentValuePath="StudentName"
                    ItemsSource="{Binding StudentList}"
                    Title="{Binding TitleList[0]}"/>
        <toolkit:ColumnSeries DependentValuePath="Project2Score" 
                    IndependentValuePath="StudentName"
                    ItemsSource="{Binding StudentList}"
                    Title="{Binding TitleList[1]}"/>
        <toolkit:ColumnSeries DependentValuePath="Project3Score"
                    IndependentValuePath="StudentName"
                    ItemsSource="{Binding StudentList}"
                    Title="{Binding TitleList[2]}"/>
        </toolkit:Chart>
    </Grid>
</navigation:Page>

And the chart looks like this:

image

Notice that the legend is still blank. That will be fixed in a later post.

Use the techniques described here any time you want to build a chart and still follow the MVVM design pattern.

Enjoy!

3 Comments

  1.   Joe — February 21, 2011 @ 1:34 am    Reply

    I’ve made it this far without blowing up my computer 🙂

    Nice intro into SilverLight and MVVM.

    A question about the charting, using your example. I added a new Chart page, but also added a Chart to an existing page with a DataGrid.

    I’m guessing if I want to update the Chart in real-time – meaning if I change a score value in the DataGrid – I would need to add the INotifyPropertyChanged, event, etc to the Student class also? That would be a nice feature.

  2.   Becky — September 19, 2011 @ 3:48 pm    Reply

    That’s going to make things a lot esaeir from here on out.

  3.   Thomas — October 27, 2011 @ 12:04 pm    Reply

    Well, tried to do just this, in another application, didn’t work at all, made my self some dinner and opened a bottle of redwine.

    Then realised that I made my “StudentList” property private instead of public.

    Ooohh welll…, anyway thanks for great post !!

RSS feed for comments on this post. TrackBack URI

Leave a comment

© 2019 Deborah's Developer MindScape   Provided by WPMU DEV -The WordPress Experts   Hosted by Microsoft MVPs