Monday, August 1, 2011

[WPF]–How to select checkbox of items in ListBox by clicking text

In WPF, you can create a checked list box by creating a normal ListBox and using an ItemTemplate as follows:
<ListBox Margin="10" HorizontalAlignment="Stretch" Name="lbSheets" 
              VerticalAlignment="Stretch" Width="Auto" Grid.Row="1" MinWidth="321"
              MinHeight="40" HorizontalContentAlignment="Left" 
              ItemTemplate="{StaticResource ListBoxItemTemplate}" VerticalContentAlignment="Top" Background="#FFDCEBEE" SelectionMode="Single" SelectionChanged="lbSheets_SelectionChanged">
 
     </ListBox>


<DataTemplate x:Key="ListBoxItemTemplate" >
 
     <WrapPanel>
         <CheckBox Focusable="False" IsChecked="{Binding Selected}" VerticalAlignment="Center" />
         <ContentPresenter  Content="{Binding FullName, Mode=OneTime}"  Margin="2,0" />
     </WrapPanel>
 
 </DataTemplate>

In my example, I am then binding a domain model object that implements INotifyPropertyChanged, and I have a boolean property for Selected, when the checkbox is checked, it will toggle this value, and vice versa. The text that is displayed is from a property called ‘FullName’, as specified in the DataTemplate.
image

However, I wanted to be able to check the checkboxes by clicking on the text next to them, not by pinpointing the check box. I tried a number of ways of doing this before finally arriving at a solution.
I ended up suing the ‘MouseUp’ event on the ContentPresenter from my DataTemplate
<DataTemplate x:Key="ListBoxItemTemplate" >
 
      <WrapPanel>
          <CheckBox Focusable="False" IsChecked="{Binding Selected}" VerticalAlignment="Center" />
          <ContentPresenter  Content="{Binding FullName, Mode=OneTime}"  Margin="2,0" MouseUp="ContentPresenter_MouseUp" />
      </WrapPanel>
 
  </DataTemplate>
And in this method I did the following:
private void ContentPresenter_MouseUp(object sender, MouseButtonEventArgs e)
{
    string text = ((TextBlock) e.OriginalSource).Text;
 
    foreach (var item in this.lbSheets.Items)
    {
        PrintableSheet sheetItem = (PrintableSheet) item;
        if (sheetItem.FullName.Equals(text))
        {
            sheetItem.Selected = !sheetItem.Selected;
        }
    }
 
}
I used the OriginalSource proprety from the event arguments to get the TextBlock, I used its text, and matched it up with my domain object (PrintableSheet) using the property that was bound. So now when you click the text on the item, it will toggle the checkbox.

5 comments:

Chris Buckreus said...

Very timely - I have been using a rather hacked-together combination of ListView & GridView to achieve a similar affect. This is nice and simple.

One question - I have tried this solution here, but I can't get the MouseUp event to check the box. In debugging, all the (boolean) variables get toggled correctly, but the checkbox does not visually update in the window.

Any ideas?

FWIW: My application for this "CheckListBox" is to add and remove filters to a datagrid.

Sushant said...

Excellent article Rod...Can u please provide full code for this example also with this article...I have to achieve similar functionality of clicking on textbox content & toggling the checkbox's check state but with some differences...Kindly revert back ASAP.

darrellp said...

You can also get the checkbox directly by casting the sender as ContentPresenter, casting it's parent as WrapPanel, casting it's first child as CheckBox and then setting IsChecked on the checkbox. Should be faster than searching through all your internal data and allows for some items to have identical names. A little more dependent on the UI but you're already making assumptions there.

Unknown said...

Why not use content of the checkbox for the text instead. The a single click on the text will result in checking/unchecking the checkbox.

Like:

Unknown said...

Why not use content of the checkbox for the text instead? The a single click on the text will result in checking/unchecking the checkbox.

Like: