This blog post explains how to add CheckBox controls to the
record selectors in XamDataGrid. We will
also see how to add a CheckBox to the header area above the record selectors,
so that you can have Check All / Uncheck All behavior. You
can download the demo application here.
The screenshot below shows the demo application in action:

If you were to check Ned's CheckBox, all of the records
would be in the checked state, so the header CheckBox would automatically enter
the checked state. If you toggle the
state of the header CheckBox, all of the other CheckBoxes beneath it would
assume that new check state. If you
click the button on the bottom, a MessageBox would show you which items are
checked and unchecked.
In my demo app, the data objects are of type Person, as seen
below:
/// <summary>
/// A simple data object that stores
/// information about a person.
/// </summary>
public class Person
{
public static Person[] GetPeople()
{
return new Person[]
{
new Person("Jane", 23),
new Person("Mike", 45),
new Person("Ned", 67),
};
}
public Person(string name, int age)
{
this.Name = name;
this.Age = age;
}
public int Age { get; private set; }
public string Name { get; private set; }
}
Notice that the Person class does not have any notion of
being "checked." This makes sense,
because a person in the real world is not checked or unchecked. In order to extend the semantics of a person,
and make changes to a person observable to other objects, I created a PersonViewModel
class. That class is below:
/// <summary>
/// A presentation-friendly wrapper for the Person
/// class, which has support for being 'checked.'
/// </summary>
public class PersonViewModel : INotifyPropertyChanged
{
readonly Person _person;
bool _isChecked;
public PersonViewModel(Person person)
{
_person = person;
}
public bool IsChecked
{
get { return _isChecked; }
set
{
if (value == _isChecked)
return;
_isChecked = value;
this.OnPropertyChanged("IsChecked");
}
}
public int Age { get { return _person.Age; } }
public string Name { get { return _person.Name; } }
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string prop)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
#endregion
}
The XamDataGrid displays a list of PersonViewModel
objects. The DataItem property of each
DataRecord in the grid references a PersonViewModel. The following Style exists in the XamDataGrid's
Resources collection, applying a CheckBox to the record selector of each row.
<Style TargetType="{x:Type igDP:RecordSelector}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type igDP:RecordSelector}">
<CheckBox
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsChecked="{Binding Path=DataItem.IsChecked}" >
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The collection of PersonViewModel objects to which the XamDataGrid
is bound exists in an instance of my CommunityViewModel class. That class serves two purposes. First, it contains the list of
PersonViewModel objects in its Members property. Second, it provides a get/set property called
AllMembersAreChecked. This property is
used to maintain the state of the CheckBox in the header area above the record
selectors, and to update the IsChecked property of all PersonViewModel objects
when the property is set. Here is the
CommunityViewModel class:
public class CommunityViewModel : INotifyPropertyChanged
{
public CommunityViewModel(List<PersonViewModel> members)
{
this.Members = members;
foreach (PersonViewModel member in members)
member.PropertyChanged += delegate
{
this.OnPropertyChanged("AllMembersAreChecked");
};
}
public List<PersonViewModel> Members { get; private set; }
public bool? AllMembersAreChecked
{
get
{
bool? value = null;
for (int idx = 0; idx < this.Members.Count; ++idx)
{
if (idx == 0)
{
value = this.Members[0].IsChecked;
}
else if (value != this.Members[idx].IsChecked)
{
value = null;
break;
}
}
return value;
}
set
{
if (value == null)
return;
foreach (PersonViewModel member in this.Members)
member.IsChecked = value.Value;
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string prop)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
#endregion
}
The Style used to apply a CheckBox to the header area is
quite similar to the one we saw previously.
One difference is that this Style explicitly sets the target element's
Visibility property to ‘Visible', otherwise we would never see the
CheckBox. That Style is below:
<Style TargetType="{x:Type igDP:HeaderPrefixArea}">
<Setter Property="Visibility" Value="Visible" /> <Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type igDP:HeaderPrefixArea}">
<CheckBox
HorizontalAlignment="Center"
VerticalAlignment="Center"
IsChecked="{Binding Path=DataPresenter.DataContext.AllMembersAreChecked}" >
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Download the demo project here. The demo was built in Visual Studio 2008, using NetAdvantage for WPF v8.1.
Posted
09-04-2008 12:44 PM
by
joshsmith