Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

January 22, 2011

Populating a DataGrid in a Silverlight Application using MVVM

Filed under: C#,Silverlight,VB.NET @ 7:04 pm

Model View View/Model (MVVM) is an effective pattern for building Silverlight applications. It provides many useful benefits when building business applications with Silverlight.

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.

MVVM requires breaking your Silverlight application into three pieces:

1) View: The user interface that the user sees and interacts with.

2) Model: The representation of the data used by the application.

3) View/Model: The code that provides the communication between the view and the model. This code massages the model into a format required by the view and handles events from the view to perform processing and interact with the data from the model.

There are many reasons to use MVVM. Bing "Silverlight MVVM" and you will find many good articles/posts on the merits of MVVM. The key reasons often sited are:

  • Provides a separate of concerns between the visual parts of the application (View), the processing (View/Model), and the data (Model).
  • Allows for testing of the View/Model logic without need to execute a user interface.
  • Provides a mechanism for design-time data.

And another reason that is often not mentioned is my teenager’s favorite: Everyone else is doing it. Why would that matter? Many Silverlight samples now use MVVM and many other developers posting on the forums or blogs now use MVVM. So the more you understand MVVM, the easier it may be to find help.

(And I don’t know about you, but I seem to spend a lot of my development time these days hunting down how-to’s for getting Silverlight to perform some challenging client-request feature. I hope some day soon I will get Silverlight to bow down to my every command … but today is not that day.)

So let’s start with one of the simplest MVVM applications that we can.

This example populates a simple DataGrid and produces the same results as this prior post that uses code-behind instead of MVVM.

The basic steps for populating a DataGrid in a Silverlight application using MVVM are:

1) Drag and drop a DataGrid control from the toolbox onto the desired page.

2) Build/generate the business objects that contains the data for the grid.

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

4) Hook the View to the View/Model.

5) Bind the DataGrid to a View/Model property.

Step 1 is self explanatory. If you are working through the set of blog posts, add the DataGrid to the Students page. Otherwise, add a DataGrid to any page.

Be sure to set the AutoGenerateColumns property to True so it will automatically show your data.

The Silverlight page is called a "View" because it is the user’s view of the application. It is the part of the application that the user sees and interacts with.

Step 2 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 builds an entity class in the Silverlight application that is populated from code to keep things simple.

Regardless of the approach, the entity class in your Silverlight application (built or generated) that defines the structure of the data is called the "Model" because it models, or represents, the application’s data.

Add a Models folder to your Silverlight project. In that folder, add a Student class.

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 requires building a new class that is the View/Model.

Add a ViewModels folder to your Silverlight project. In that folder, add a StudentViewModel class.

Implement INotifyPropertyChanged in the ViewModel class. That 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.

In C#:

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

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

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

        public StudentViewModel()
        {
            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;
        }

    }
}

In VB:

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

Namespace ViewModels
    Public Class StudentViewModel
        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

        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
        End Sub

    End Class
End Namespace

Step 4 hooks the View to the View/Model. There are several choices for hooking them together.

  • Option 1: Use the code behind.
    You can use the code behind to create an instance of the View/Model and assign it as the DataContext of the page.

    The plus side of this option is that there is then an easy way to handle events. You can create a standard event procedure in your code behind and use the View/Model instance to call a method in the View/Model to perform the event processing.

    The down side of this approach is that you won’t get the design-time data because the designer does not execute the code behind’s constructor.

    The View’s code behind would look something like this:

In C#:

using System.Windows.Controls;
using System.Windows.Navigation;
using InStepSM.SL.ViewModels;

namespace InStepSM.SL.Views
{
    public partial class Overview : Page
    {
        public StudentViewModel vm { get; set; }

        public Overview()
        {
            InitializeComponent();
            vm = new StudentViewModel();
            this.DataContext = vm;
        }

        private void dataGrid1_SelectionChanged(object sender,
                                SelectionChangedEventArgs e)
        {
            vm.SomeMethod();
        }

    }
}

In VB:

Imports InStepSM.SL.ViewModels

Partial Public Class Overview
    Inherits Page

    Public Property vm As StudentViewModel

    Public Sub New()
        InitializeComponent()
        vm = New StudentViewModel()
        Me.DataContext = vm
    End Sub

    Private Sub dataGrid1_SelectionChanged(ByVal sender As Object, 
       ByVal e As System.Windows.Controls.SelectionChangedEventArgs) _
       Handles dataGrid1.SelectionChanged
        vm.SomeMethod()
    End Sub
End Class

And the xaml for the DataGrid would look something like this:

<sdk:DataGrid AutoGenerateColumns="true"
      ItemsSource="{Binding StudentList}"
      Height="200"
      Name="dataGrid1"
      Width="500"
      SelectionChanged="dataGrid1_SelectionChanged" />

Where sdk is:

xmlns:sdk=http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk

  • Option 2: Define the instance of the View/Model in the xaml.
    This is the most common approach because it minimizes the amount of code required in the code behind and provides for design-time data.

    In this option, the View’s code behind is basically empty:

In C#:

using System.Windows.Controls;
using System.Windows.Navigation;

namespace InStepSM.SL.Views
{
    public partial class Overview : Page
    {
        public Overview()
        {
            InitializeComponent();
        }
    }
}

In VB:

Partial Public Class Overview
    Inherits Page

    Public Sub New()
        InitializeComponent()
    End Sub

End Class

The xaml contains the reference to the View/Model, shown in Red:

<navigation:Page x:Class="InStepSM.SL.Views.Overview"        

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"
xmlns:sdk="
http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
d:DesignWidth="640" d:DesignHeight="480"
Title="Overview Page" >

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

</navigation:Page>

The above vms namespace line references the namespace for our View/Model. The DataContext lines set the DataContext of the page to the StudentViewModel. When the designer executes this xaml, it creates an instance of the StudentViewModel class, which in turn executes the constructor. So any code in the View/Model’s constructor is executed by the designer.

Step 5 binds the property in the View/Model to the DataGrid in the xaml.

<sdk:DataGrid AutoGenerateColumns="true"
      ItemsSource="{Binding StudentList}"
      Height="200"
      Name="dataGrid1"
      Width="500" />

VoilĂ ! The grid data now appears in the designer:

image

And the resulting application appears as follows:

image

This post provides the very basics of using MVVM in a Silverlight application.

There are several frameworks available for working with MVVM, including MVVMLight. But trying MVVM manually first may help you better understand and leverage MVVM frameworks.

Enjoy!

EDIT 1/23/11: Inserted missing code example.

EDIT 1/25/11: Corrected a few lines that were cut off. Added information on setting the AutoGenerateColumns property of the DataGrid to True.

11 Comments

  1.   nizam deen — January 6, 2012 @ 6:53 am    Reply

    Very nice post. I can able to understand very short time span.

    Thank you very much

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