Adding a “Select All” Checkbox to a XamGrid CheckboxColumn Header

[Infragistics] Devin Rader / Friday, April 29, 2011

Using checkboxes in a grid is a common way of allowing end users to select multiple objects in the grid, which the application can then act on later.  Its also common to see a “Select All” checkbox in the header of a column that lets the end user change the selected state of all of the grid rows using a single checkbox.  In this article I will show you how to customize the Header of a CheckBoxColumn in the XamGrid to display a “Select All” checkbox and show how to use commands to change the selected state of all of the checkboxes in the column based on the value of the Header checkbox.

Project Setup

To get started, I’ve created a simple WCF service in my ASP.NET application that returns the Customers table from a fake Northwind database I have included in my project.  This service will serve as the data source for the XamGrid in my Silverlight application.  Once the service is created I simply add the service reference to my Silverlight project.

Extending the Customer Model

The Northwind Customers table by default does not include an attribute that lets me track the selected state of a Customer.  Since I want to use a CheckBoxColumn in my grid to allow users to select specific customers, I need to find some way of adding a property to the Customer object which lets me use a CheckBoxColumn to indicate the customers selected state.  I could do this using something like an UnboundColumn, but then I would have to loop through the Grids SelectedRows collection in my View and somehow map those Rows back to their underlying data objects.  Instead, a better solution is to simply extend my Model by adding a new property to the Customer class in my Silverlight application using a partial class:

public partial class Customer 
{ 
    private bool _isSelected; 

    public bool IsSelected 
    { 
        get 
        { 
            return this._isSelected; 
        } 
        set 
        { 
            if ((object.ReferenceEquals(this._isSelected, value) != true)) 
            { 
                this._isSelected = value; 
                this.RaisePropertyChanged("IsSelected"); 
            } 
        } 
    } 
}

Now when the collection of Customers from Northwind is bound to the XamGrid, a CheckBoxColumn will shown in the grid that allows to toggle the value of the Customers IsSelected property.

Creating a View and ViewModel

Once my Model is set up I need to create a View and ViewModel to display my data.  My View is simply a Silverlight navigation page that contains an instance of the XamGrid.  The View also creates an instance of the ViewModel and sets it as the Page’s DataContext.

<ig:XamGrid ItemsSource="{Binding Customers}" AutoGenerateColumns="False"> 
    <ig:XamGrid.Columns> 
        <ig:CheckBoxColumn Key="IsChecked" Width="40" />            
        <ig:TextColumn Key="CompanyName" /> 
    </ig:XamGrid.Columns> 
    <ig:XamGrid.EditingSettings> 
        <ig:EditingSettings AllowEditing="Cell" /> 
    </ig:XamGrid.EditingSettings> 
</ig:XamGrid>

The ViewModel is responsible for getting the Customers data from the WCF service and exposes it as a collection of Customers.

public class CustomersViewModel : ViewModelBase
{
    private ObservableCollection<Customer> _customers;

    public CustomersViewModel()
    {
        NorthwindClient client = new NorthwindClient();
        client.GetCustomersCompleted +=
            new EventHandler<GetCustomersCompletedEventArgs>(
                client_GetCustomersCompleted);
        client.GetCustomersAsync();
    }

    void client_GetCustomersCompleted(object sender, 
                                      GetCustomersCompletedEventArgs e)
    {
        this.Customers = e.Result;
    }

    public ObservableCollection<Customer> Customers
    {
        get { return this._customers; }
        set
        {
            if (value != this._customers)
            {
                this._customers = value;
                this.RaisePropertyChanged("Customers");
            }
        }
    }
}

If I run the project at this point you will see I get a fairly standard grid of Customers, including the IsSelected property I added using the partial class.

Now I want to change the Header of the CheckBoxColumn to include my “Select All” checkbox.  I can do this using the CheckBoxColumns HeaderTemplate.

<ig:XamGrid ItemsSource="{Binding Customers}" AutoGenerateColumns="False"> 
    <ig:XamGrid.Columns> 
        <ig:CheckBoxColumn Key="IsChecked" Width="40"> 
            <ig:CheckBoxColumn.HeaderTemplate> 
                <DataTemplate> 
                    <CheckBox  /> 
                </DataTemplate> 
            </ig:CheckBoxColumn.HeaderTemplate> 
        </ig:CheckBoxColumn> 
        <ig:TextColumn Key="CompanyName" /> 
    </ig:XamGrid.Columns> 
    <ig:XamGrid.EditingSettings> 
        <ig:EditingSettings AllowEditing="Cell" /> 
    </ig:XamGrid.EditingSettings> 
</ig:XamGrid>

At the same time I have enabled editing on the cells in the grid to allow me to check the checkboxes on the individual grid rows.

Running the sample now you can see that the checkbox shows as expected in the column header, but checking it does nothing.  I want to use a Command which will execute when the checkboxes IsChecked is toggled and change the value of the IsSelected property of all of my Customers.  To do this I am going to use a RelayCommand object which is included in the MVVM Light toolkit.

As a side note, the MVVM Light Toolkit can also be added using the NuGet Package Manager which makes it super easy to add this great toolkit to your project.  The Nuget Package Manager is itself available as an extension from the Visual Studio Gallery 

To add the command I add a new property to my ViewModel called SelectAllCustomers.  This property returns a RelayCommand<bool>, where the Boolean value is the type of the CommandParameter passed into the command.  When this command is executed it will loop through the Customers collection and sets the IsSelected property of each Customer.

private RelayCommand<bool> _selectallcustomers;
public RelayCommand<bool> SelectAllCustomers {
    get
    {
        if (_selectallcustomers == null)
        {
            _selectallcustomers = new RelayCommand<bool>((chk) =>
            {
                this.Customers.ToList().ForEach(c => c.IsSelected = (bool)chk);
            });
        }

        return _selectallcustomers;
    }
}

Now I need to bind the command to the checkbox, but as you may know the Columns in XamGrid don’t inherit the Data Context form their parent, so I need to use the DataContextProxy trick, which I blogged about earlier.

Once I set up the static reference to the DataContextProxy object, I can reference this static resource in the CheckBoxColumns HeaderTemplate and set the binding on the Checkboxes Command property:

<ig:CheckBoxColumn.HeaderTemplate> 
    <DataTemplate> 
        <CheckBox Command="{Binding Path=ProxiedObject.CheckAllCustomers, 
                               Source={StaticResource proxy}}" 
            CommandParameter="{Binding Path=IsChecked, 
                               RelativeSource={RelativeSource Self}}" /> 
    </DataTemplate> 
</ig:CheckBoxColumn.HeaderTemplate> 

Now when I run the sample, I can click the checkbox in the header and all of the checkboxes in the row will update, reflecting the change in each objects IsSelected value.

Conclusion

That’s all there is to setting up a “Select All” checkbox in the header of a XamGrid column.  You can see that by combining some standard features of .NET with a great toolkit like MVVM Light and a but of Data Context trickery, its easy to create this feature following the MVVM pattern.

The complete source for this example can be found here.