19
votes

I have a listbox where the items contain checkboxes:

<ListBox Style="{StaticResource CheckBoxListStyle}" Name="EditListBox">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox Click="Checkbox_Click" IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" Content="{Binding Path=DisplayText}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

The problem I'm having is that when I click on the checkbox or its content, the parent ListBoxItem does not get selected. If I click on the white space next to the checkbox, the ListBoxItem does get selected.

The behavior that I'm trying to get is to be able to select one or many items in the list and use the spacebar to toggle the checkboxes on and off.

Some more info:

private void Checkbox_Click(object sender, RoutedEventArgs e)
{
    CheckBox chkBox = e.OriginalSource as CheckBox;
}

In the code above when I click on a checkbox, e.Handled is false and chkBox.Parent is null.

Kent's answer put me down the right path, here's what I ended up with:

<ListBox Style="{StaticResource CheckBoxListStyle}" Name="EditListBox" PreviewKeyDown="ListBox_PreviewKeyDown">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <CheckBox IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" />
                <TextBlock Text="{Binding DisplayText}"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

I had to use PreviewKeyDown because by default when you hit the spacebar in a list box, it deselects everything except for the most recently selected item.

3

3 Answers

10
votes

To begin with, put the content outside the CheckBox:

<StackPanel Orientation="Horizontal">
    <CheckBox IsChecked="{Binding IsChecked}"/>
    <TextBlock Text="{Binding DisplayText}"/>
</StackPanel>

After that, you will need to ensure that pressing space on a ListBoxItem results in the CheckBox being checked. There are a number of ways of doing this, including a simple event handler on the ListBoxItem. Or you could specify a handler for UIElement.KeyUp or whatever in your DataTemplate:

<CheckBox IsChecked="{Binding IsChecked}" UIElement.KeyUp="..."/>
3
votes

You can also bind the IsChecked property of the CheckBox and IsSelected property of the ListBoxItem:

<ListBox>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox Content="{Binding DisplayText}" IsChecked="{Binding Path=IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
3
votes

In your use case it would be way simpler to use a ItemsControl instead of a list box. A ItemsControl is similar to a Listbox except that it doesn't contain the automatic selection behaviour. Which means that using it to host a list of what are essentially checkboxes is very simple and you don't have to workaround the ListBox's selection behaviour.

Simply switching to ItemsControl will give you exactly what you need:

<ItemsControl Style="{StaticResource CheckBoxListStyle}" Name="EditListBox">
    <ItemsControl .ItemTemplate>
        <DataTemplate>
            <CheckBox Click="Checkbox_Click" IsChecked="{Binding Path=IsChecked, Mode=TwoWay}" Content="{Binding Path=DisplayText}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

You can click on text to check checkboxes (default behavior) and you can use the keyboard too without having to wire up any event handlers.