Deborah's Developer MindScape






         Tips and Techniques for Web and .NET developers.

February 12, 2011

Silverlight Simple MVVM Commanding

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

As you build your line of business (LOB) applications, you will need to process commands. In many cases, these commands are executed when the user clicks a button. For example, when the user clicks the Save button, the code needs to process a Save "command" and perform the save.

One way to handle the commands in your application is in the code behind. Just double-click on the button in the Visual Studio designer and type code into the resulting event handler. But this technique does not work if you want to follow Model-View-View/Model (MVVM).

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.

NOTE: If you are new to MVVM, this prior post provides an introduction.

Luckily Silverlight 4 provides for commanding, but only with buttons and hyperlinks. This post describes how to use the built-in commanding for these simplest scenarios. (A later post will cover more complex scenarios.)

Using the built-in commanding feature in a Silverlight MVVM application requires three basic steps:

1) Create a command class that implements the ICommand interface.

2) Create a property of type ICommand in the View/Model class.

3) Bind the Command property of a Button or Hyperlink to the property created in 2 above.

Creating a Command Class

The first step to using the commanding feature in a Silverlight MVVM application is to create a command class for each command. Considering how many commands you may have in a LOB application, this step will get boring very quickly. StudentSaveCommand, StudentAddCommand,  StudentDeleteCommand, ScoreSaveCommand, ScoreAddCommand, etc, etc you get the picture.

So a better option is to create one generalized command class that you can use for all of your commands. This is what the Prism framework did, what many other blog posts on this topic did, and what this post will do.

The code is shown in C# and VB, and then explained.

In C#:

using System;
using System.Windows.Input;

namespace InStepSM.Library
{
    ///<summary>
    /// Gneric ICommand implementation to assist with processing
    /// commands in MVVM.
    ///</summary>

    public class DelegateCommand<T> : ICommand
    {
        /// <summary>
        /// Defines the method for the action to be executed.
        /// </summary>
        private readonly Action<T> executeAction;

        /// <summary>
        /// Defines the function that determines whether the
        /// action can be executed.
        /// </summary>
        private readonly Func<T,bool> canExecuteAction;

        ///<summary>
        /// Defines an event to raise when the values that
        /// affect "CanExecute" are changed.
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Constucts an object that can always execute.
        /// </summary>
        /// <param name="currentExecuteAction"></param>
        /// <remarks></remarks>
        public DelegateCommand(Action<T> executeAction)
            : this(executeAction, null)
        {
        }

        /// <summary>
        /// Constructs an object to execute.
        /// </summary>
        public DelegateCommand(Action<T> executeAction,
            Func<T,bool> canExecuteAction)
        {
            this.executeAction = executeAction;
            this.canExecuteAction = canExecuteAction;
        }

        /// <summary>  
        /// Defines the method that determines whether the command can
        /// execute in its current state.  
        /// </summary>   
        public bool CanExecute(object parameter)
        {
            if (canExecuteAction != null)
            {
                return canExecuteAction((T)parameter);
            }
            return true;
        }

        /// <summary>  
        /// Defines the method to call when the command is invoked.  
        /// </summary>   
        public void Execute(object parameter)
        {
            if (CanExecute(parameter))
            {
                executeAction((T)parameter);
            }
        }
    }
}

In VB:

Imports System.Windows.Input

”’ <summary>
”’ Generic ICommand implementation to assist with processing
”’ commands in MVVM
”’ </summary>
Public Class DelegateCommand(Of T)
    Implements ICommand

    ”’ <summary>
    ”’ Defines the subroutine for the action to be executed.
    ”’ </summary>
    Private ReadOnly executeAction As Action(Of T)

    ”’ <summary>
    ”’ Defines the function that determines whether the 
    ”’ action can be executed.
    ”’ </summary>
    Private ReadOnly canExecuteAction As Func(Of T, Boolean)

    ”’ <summary>
    ”’ Defines an event to raise when the values that
    ”’ affect "CanExecute" are changed
    ”’ </summary>
    Public Event CanExecuteChanged(ByVal sender As Object, 
                          ByVal e As System.EventArgs) _
         Implements System.Windows.Input.ICommand.CanExecuteChanged

    ”’ <summary>
    ”’ Constructs an object that can always execute.
    ”’ </summary>
    Public Sub New(ByVal currentExecuteAction As Action(Of T))
        Me.New(currentExecuteAction, Nothing)
    End Sub

    ”’ <summary>
    ”’ Constructs an object to execute.
    ”’ </summary>
    Public Sub New(ByVal currentExecuteAction As Action(Of T),
            ByVal currentCanExecuteAction As Func(Of T, Boolean))
        executeAction = currentExecuteAction
        canExecuteAction = currentCanExecuteAction
    End Sub

    ”’ <summary>
    ”’ Defines the method that determines whether the command can 
    ”’ execute in its current state.
    ”’ </summary>
    Public Function CanExecute(ByVal parameter As Object) As Boolean _
                 
Implements System.Windows.Input.ICommand.CanExecute
        If canExecuteAction IsNot Nothing Then
            Return canExecuteAction(DirectCast(parameter, T))
        Else
            Return True
        End If
    End Function

    ”’ <summary>
    ”’ Defines the method to be called when the command is invoked.
    ”’ </summary>
    Public Sub Execute(ByVal parameter As Object) _
              Implements System.Windows.Input.ICommand.Execute
        If CanExecute(parameter) Then
            executeAction(DirectCast(parameter, T))
        End If
    End Sub
#End Region

End Class

The class name, DelegateCommand, is the name used by Prism and the name selected for this post. It is a generic class with T representing the type of the parameter passed to the command, if any.

The executeAction property is a delegate that defines a method to execute when the command is invoked. For a Save command for example, the executeAction property may be SaveStudent(). So when the user clicks the Save button, the SaveStudent method in your View/Model is executed.

The canExecuteAction property is a delegate that defines a method that determines whether or not the executeAction method can be executed based on the current state of the application. For a Save command for example, the canExecuteAction property may be Validate(). So the SaveStudent method won’t be executed if the student is not valid.

The CanExecuteChanged defines an event to raise when the values that affect the canExecutAction changes. This is beyond the scope of this post but will be further detailed in a later post.

The constructors construct an instance of the command either with just an executeAction, or with both an executeAction and a canExecuteAction.

The CanExecute method executes the delegate defined in the canExecuteAction property, passing in any parameter. If the property is not set, it always returns true so the method can be executed.

NOTE: If you have a Button in the UI bound to a command and the canExecuteAction method returns false, Silverlight will automatically disable the button since the action cannot be executed.

The Execute method first calls the CanExecute method to ensure that the command can be executed. If that method returns true, it executes the delegate defined in the executeAction, passing in any parameter.

Creating a Property in the View/Model

Add three things to the View/Model for each command:

1) Command property.

2) Code to set the command property.

3) Method defined for the command property delegate.

The code is shown with the statements for 1, 2, and 3 above shown in red.

In C#:

using System;
using System.Collections.ObjectModel;
using System.ServiceModel.DomainServices.Client;
using System.Windows.Input;
using InStepSM.Library;
using InStepSM.SL.Web;

namespace InStepSM.SL.ViewModels
{
    public class StudentsViewModel : ViewModelBase
    {
        private ICommand _SaveCommand;
        public ICommand SaveCommand
        {
            get
            {
                return _SaveCommand;
            }
        }

        private StudentContext StudentContextInstance;

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

        public StudentsViewModel()
        {
            LoadData();
            DefineCommands();
        }

        private void DefineCommands()
        {
            _SaveCommand = new DelegateCommand<Student>
                            (OnSaveCommand);
        }

        private void LoadData()
        {
            if (IsInDesignModeStatic)
            {
                LoadDesignData();
            }
            else
            {
                // Load the student data asynchronously
                StudentContextInstance = new StudentContext();
                var loadop =
                    StudentContextInstance.Load(StudentContextInstance.
                                    GetStudentsQuery(),
                                    OnStudentsLoaded, null);
            }
        }

        private void LoadDesignData()
        {
            //Design mode data
            ObservableCollection<Student> temp =
                         new ObservableCollection<Student>();
            temp.Add(new Student
            {
                LastName = "Baggins",
                FirstName = "Bilbo",
                Age = 111,
                Email = "testemail@live.com",
                RegistrationDate = DateTime.Now
            });
            temp.Add(new Student
            {
                LastName = "Baggins",
                FirstName = "Frodo",
                Age = 32,
                Email = "testemail@yahoo.com",
                RegistrationDate = DateTime.Now
            });
            StudentsList = temp;
        }


        private void OnStudentsLoaded(LoadOperation lo)
        {
            StudentsList = 
    new ObservableCollection<Student>(StudentContextInstance.Students);
        }

        private void OnSaveCommand(Student s)
        {
            StudentContextInstance.SubmitChanges();
        }

    }
}

In VB:

Imports System.Collections.ObjectModel
Imports System.ServiceModel.DomainServices.Client
Imports InStepSM.SL.Web

Namespace ViewModels
    Public Class StudentsViewModel
        Inherits ViewModelBase

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

        Private StudentContextInstance As StudentContext

        Private _SaveCommand As ICommand
        Public ReadOnly Property SaveCommand() As ICommand
            Get
                Return _SaveCommand
            End Get
        End Property

        Public Sub New()
            LoadData()
            DefineCommands()
        End Sub

        Private Sub DefineCommands()
            _SaveCommand = New DelegateCommand(Of Student)(
                            AddressOf OnSaveCommand)
        End Sub

        Private Sub LoadData()
            If IsInDesignModeStatic Then
                LoadDesignData()
            Else
                ‘ Load the student data asynchronously
                StudentContextInstance = New StudentContext
                Dim loadop =
                  StudentContextInstance.Load(StudentContextInstance.
                                GetStudentsQuery(),
                                AddressOf OnStudentsLoaded, Nothing)
            End If
        End Sub

        Private Sub LoadDesignData()
            ‘ Design mode data
            Dim temp As New ObservableCollection(Of Student)
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Bilbo",
                                       .Age = 111,
                                       .Email = "testemail@live.com",
                                       .RegistrationDate = Now()})
            temp.Add(New Student With {.LastName = "Baggins",
                                       .FirstName = "Frodo",
                                       .Age = 32,
                                       .Email = "testemail@yahoo.com",
                                       .RegistrationDate = Now()})
            StudentsList = temp

        End Sub

        Private Sub OnStudentsLoaded(ByVal lo As LoadOperation)
            StudentsList = 
  New ObservableCollection(Of Student)(StudentContextInstance.Students)
        End Sub

        Private Sub OnSaveCommand(ByVal s As Student)
            StudentContextInstance.SubmitChanges()
        End Sub

    End Class
End Namespace

The SaveCommand is the property that implements ICommand. The UI can be bound to this property.

The OnSaveCommand is the method to be executed when the command occurs.

The SaveCommand is set to an instance of the DelegateCommand, passing in OnSaveCommand as the delegate.

Note that in this example, the parameter is not used. Since the entire grid is saved in a save operation in this example, there is no processing needed for the selected student.

Binding to the Command

The last step is the easiest: just bind the Command property of the Button (or Hyperlink) to the command property created in the prior step.

<Button x:Name="SaveButton"
        Grid.Column="1"
        Command="{Binding SaveCommand}"
        CommandParameter="{Binding SelectedItem,
                           ElementName=studentDataGrid}"
        Content="Save"
        HorizontalAlignment="Right"
        Margin="10,10,0,10"
        Width="60"/>

The Command property of the Button is bound to the SaveCommand property in the View/Model. When the button is clicked, the Execute method of the DelegateCommand is executed which in turn calls the OnSaveCommand method in the View/Model. This method ultimately calls the SubmitChanges method, saving any changes.

The CommandParameter property of the Button is bound to the SelectedItem of the DataGrid. This passes the currently selected Student to the command.

In this specific example, the parameter is not used so you only really need to set the Command property, not the CommandParameter property.

Use this technique any time you need to process commands from the Button or Hyperlink controls. (Or any other control that inherits from ButtonBase.) If you need to process commands from other controls, such as a click on a grid row, you need more code. But that is for a later post.

Enjoy!

8 Comments

  1.   ali — February 21, 2011 @ 6:48 am    Reply

    Deborah Kurata,
    very helpfull post.a help req from ur side.I have a
    registration form just 4 fields.
    firstname txtfirstname
    lastname txtlastname
    email txtemail
    telephone txtphone
    and one save button
    now i design the above into silverlight page.
    when i click on save button , the command should go to the view model and from view model it using nhibernate save into database in the register table.

    Can u help me wht type of code is in the view model so that i can proceed my proof of concept a stuck me for two days.

    ur help is very helpfull for me.

    Thnx in advance.

  2.   ali — February 21, 2011 @ 7:39 am    Reply

    Can u tell me what is in the student context class.

    Regards
    Ali

  3.   DeborahK — February 21, 2011 @ 9:42 am    Reply

    I have not used NHibernate, but I assume it is something you would use server-side … similar to Entity Framework.

    If that is the case, you can check out this post:

    http://msmvps.com/blogs/deborahk/archive/2011/01/30/accessing-data-in-a-silverlight-application-ef.aspx

    It demonstrates how to save using Entity Framework.

    Hope this helps.

  4.   DeborahK — February 21, 2011 @ 10:00 am    Reply

    Hi Ali –

    This post is part of a series. The student context class was created in this prior post:

    http://msmvps.com/blogs/deborahk/archive/2011/01/30/accessing-data-in-a-silverlight-application-ef.aspx

    Hope this helps.

  5.   Sylvie — June 28, 2011 @ 7:14 am    Reply

    I can’t find what is StudentContext. Is a class defined in an other post?

  6.   Sylvie — June 28, 2011 @ 7:17 am    Reply

    Sorry, I found it

  7.   Blue — September 19, 2011 @ 6:32 pm    Reply

    Great post with lots of imoprtant stuff.

  8.   hello — September 26, 2011 @ 3:35 am    Reply

    what was this post about?

RSS feed for comments on this post. TrackBack URI

Leave a comment

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