Dear visitor, thanks for stopping by! If you want, you can follow all updates on Snowball.be via RSS. You can also follow me on Twitter or Facebook. More interesting posts from other Microsoft Regional Directors can be found at The Region.
Gill Cleeren     mvvm | Silverlight | sl4     December 31, 2010    

When working with MVVM, the Command pattern is used to bind actions (such as a Click on a Button) in the View with commands in the ViewModel. The ICommand interface in Silverlight has 3 members:

// Summary:
//     Defines the contract for commanding, using the same contract as used in WPF.
public interface ICommand
{
    // Summary:
    //     Occurs when changes occur that affect whether the command should execute.
    event EventHandler CanExecuteChanged;
 
    // Summary:
    //     Defines the method that determines whether the command can execute in its
    //     current state.
    //
    // Parameters:
    //   parameter:
    //     Data used by the command. If the command does not require data to be passed,
    //     this object can be set to null.
    //
    // Returns:
    //     true if this command can be executed; otherwise, false.
    bool CanExecute(object parameter);
    //
    // Summary:
    //     Defines the method to be called when the command is invoked.
    //
    // Parameters:
    //   parameter:
    //     Data used by the command. If the command does not require data to be passed,
    //     this object can be set to null.
    void Execute(object parameter);
}

The fact whether or not the command can be executed (read: a Button is enabled or not) depends on the boolean value returned by the CanExecute. By default, this value is evaluated on first load of the View: a Save button should initially be disabled. However, during user input, this value has to be re-evaluated: after the user has filled in a field, we need to check if the Save button should be enabled. For this purpose, the CanExecuteChanged event exist. This event should be raised whenever the value of CanExecute should be evaluated once again.

In MVVM Light, the lightwork MVVM framework by Laurent Bugnion, a method called RaiseCanExecuteChanged exists on the RelayCommand class for this very purpose. Let’s take a look at how we can use this method.

I have a very basic view, containing some TextBox fields. Note that initially, the Save button is disabled:

image

The XAML code is shown below. The fields are bound to a Person object, that I expose in the ViewModel (shown further).

 
<Grid Width="450">
    <Grid.RowDefinitions>
        <RowDefinition Height="50"></RowDefinition>
        <RowDefinition Height="40"></RowDefinition>
        <RowDefinition Height="40"></RowDefinition>
        <RowDefinition Height="40"></RowDefinition>
        <RowDefinition Height="40"></RowDefinition>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"></ColumnDefinition>
        <ColumnDefinition Width="*"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <TextBlock x:Name="FirstNameTextBlock" Grid.Row="1" 
        Grid.Column="0" Text="First name" VerticalAlignment="Center" Margin="3"></TextBlock>
    <TextBlock x:Name="LastNameTextBlock" Grid.Row="2" 
        Grid.Column="0" Text="Last name" VerticalAlignment="Center" Margin="3"></TextBlock>
    <TextBlock x:Name="EmailTextBlock" Grid.Row="3" 
        Grid.Column="0" Text="Email" VerticalAlignment="Center" Margin="3"></TextBlock>
    <TextBox x:Name="FirstNameTextBox" Grid.Row="1" 
        Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left" Width="200" 
        Margin="3" Text="{Binding Person.FirstName, Mode=TwoWay}"></TextBox>
    <TextBox x:Name="LastNameTextBox" Grid.Row="2" Grid.Column="1" 
        VerticalAlignment="Center" HorizontalAlignment="Left" Width="200" 
        Margin="3" Text="{Binding Person.LastName, Mode=TwoWay}"></TextBox>
    <TextBox x:Name="EmailTextBox" Grid.Row="3" Grid.Column="1" 
        VerticalAlignment="Center" HorizontalAlignment="Left" Width="200" 
        Margin="3" Text="{Binding Person.Email, Mode=TwoWay}"></TextBox>
    <Button Content="Save"  Grid.Row="4" Grid.Column="1" 
        Command="{Binding SaveCommand}" VerticalAlignment="Center" 
        HorizontalAlignment="Right"></Button>
</Grid>

The ViewModel called MainPageViewModel is set as the DataContext for this View. This ViewModel exposes a Person instance (a Model class) and a RelayCommand for the View to bind to.

public class MainPageViewModel: ViewModelBase
{
    public Person Person { get; set; }
 
    public RelayCommand SaveCommand { get; set; }
 
}

The SaveCommand is initialized as follows:

private void InitializeCommands()
{
    SaveCommand = new RelayCommand(OnSave, OnCanSave);
}
 
private void OnSave()
{
    //Perform save to database
}
 
public bool OnCanSave()
{
    return !string.IsNullOrEmpty(Person.FirstName) 
        && !string.IsNullOrEmpty(Person.LastName) 
        && !string.IsNullOrEmpty(Person.Email);
}

Note that the OnCanSave (the implementation of the CanExecuteChanged) will at this point only be called upon binding of the ViewModel. However, I need this to be evaluated every time a value of the bound Person changes: only after all 3 fields have a value, the Save should be enabled.

To implement this, I changed the Person class as follows. It implements the INotifyPropertyChanged interface.

public class Person : INotifyPropertyChanged
{
    private string _firstName;
    private string _lastName;
    private string _email;
 
    public event PropertyChangedEventHandler PropertyChanged;
 
    public string FirstName
    {
        get
        {
            return _firstName;
        }
        set
        {
            if (_firstName != value)
            {
                _firstName = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("FirstName"));
                }
            }
        }
    }
 
    public string LastName
    {
        get
        {
            return _lastName;
        }
        set
        {
            if (_lastName != value)
            {
                _lastName = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("LastName"));
                }
            }
        }
    }
 
    public string Email
    {
        get
        {
            return _email;
        }
        set
        {
            if (_email != value)
            {
                _email = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Email"));
                }
            }
        }
    }
}

In the constructor of the ViewModel, I register for the PropertyChanged event being raised from the Person model.

public MainPageViewModel()
{
    InitializeCommands();
 
    Person = new Model.Person();
    Person.PropertyChanged += 
        new System.ComponentModel.PropertyChangedEventHandler(Person_PropertyChanged);
}

In this event handler, I now call the RaiseCanExecuteChanged method, part of the RelayCommand class of MVVM Light.

void Person_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    SaveCommand.RaiseCanExecuteChanged();
}

This causes the CanExecuteChanged to be raised, causing Silverlight to evaluate again if the Command should be enabled or not. In this particular case, I do this as follows (this code was already shown and executed also on the initial binding):

public bool OnCanSave()
{
    return !string.IsNullOrEmpty(Person.FirstName) 
        && !string.IsNullOrEmpty(Person.LastName) 
        && !string.IsNullOrEmpty(Person.Email);
}

Whenever a value is provided for all 3 fields, the Button will be enabled.

image

  Posted on: Friday, December 31, 2010 11:19:12 AM (Romance Standard Time, UTC+01:00)   |   Comments [0]
         
5/17/2012   4:31:59 PM
 Welcome to Snowball.be
Hello and welcome to snowball.be!

My name is Gill Cleeren, I'm a Microsoft Regional Director and an MVP ASP.NET.
On Snowball.be, you'll find all kind news and articles on .net, ASP.NET, WPF, Silverlight and Microsoft in general.
More on me can be found on my about page.

Should you have any questions, don't hesitate to contact me by Send mail to the author(s) .

 Partner sites
 Most popular tags
.net (124) .net 3.0 (6) .net 3.5 (18) .NET 4 (18) .NET Show (1) ADO.net (4) ASP.net (53) ASP.net AJAX (4) ASP.NET MVC (3) Atlas (12) Azure (2) Blend (2) Book (6) Book review (4) C# (43) Case studies (1) Chopsticks (3) Community (10) Community Day (16) Consoles (1) Database (1) DevDays09 (4) DotNetNuke (4) Efficiency (57) Enterprise Library (5) Events (60) Expression (7) Games (3) Hardware (9) Internet (18) IT (1) jQuery (1) LightSwitch (3) Links (11) LINQ (4) Mac (2) Metro (1) Microsoft (75) Mix 07 (6) Mix 08 (4) Mix 09 (1) Mix 11 (1) Movies (4) MVP (5) MVP Summit 2008 (3) mvvm (1) Office 2007 (10) Other (8) PDC (22) PDC2008 (10) Personal (36) ppt (9) Programming (52) Programming tools (22) Regional Director (2) Silverlight (143) Silverlight Advent Calendar (24) sl4 (44) SL5 Data and Services Cookbook (2) Slide decks (13) Snowball (13) Software (20) Microsoft (25) Speaking (14) SQL Server (10) TechDays (13) TechEd (14) telerik (6) Telerik (6) TFS (1) Twitter (1) Vista (73) Vista Tricks (9) Visual Studio.net (38) Visug (33) VS2010 (8) Wallpaper (2) WCF (2) Webcasts (9) Webinars (5) Windows (41) Windows 7 (5) Windows 8 (1) Windows Azure (2) Windows Mobile (3) Windows Phone 7 (2) WinFX (17) WinRT (1) WP7 (2) WPF (40) XAML (24)

 On this page
 This site
 Archives
Navigation
 Sitemap
 Blogroll OPML
 Disclaimer

All content is property of www.snowball.be. Nothing on this site can be copied or published elsewhere, unless otherwise stated.

This site is made by Gill Cleeren.

Questions? Opinions? Send mail to the author(s) E-mail