WPF ListBox Selection via CheckBox

Here's a simple solution to add check boxes to a ListBox (or other similar controls) in WPF. This solution will keep the selection of the check box in sync with the list box's SelectedItems.

TLDR

The TLDR is that you should add a CheckBox control to your DataTemplate and then bind the box's IsChecked property to the ListBoxItem.IsSelected property, using a relative source binding.

MainWindow.xaml

In this file, we've got a ListBox that is bound to an Items property on the Window. The ListBox's SelectionMode property has been set to Multiple to allow multiple items to be selected at a time. Additionally, the ListBox.ItemTemplate has been set to a custom DataTemplate which contains our CheckBox. The IsChecked property is bound to the IsSelected property of the ListBoxItem.

ListBox controls automatically create a ListBoxItem for every item provided in the ListBox.ItemsSource. Every ListBoxItem.IsSelected is kept in sync with the containing ListBox control and that IsSelected property can be modified to change what's selected within the ListBox.

By using a RelativeSource binding on our IsChecked property, we tell the CheckBox to crawl up the tree until the containing ListBoxItem is located (via the AncestorType). Once the item is located, we simply bind to the item's IsSelected property, effectively synchronizing our CheckBox.IsChecked state to the ListBoxItem.IsSelected state.

<Window x:Class="MultiSelectionSpike.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MultiSelectionSpike"
        d:DataContext="{d:DesignInstance local:MainWindow}"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <ListBox ItemsSource="{Binding Path=Items}" SelectionMode="Multiple">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <DockPanel>
                    <CheckBox IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBoxItem}, Path=IsSelected}" DockPanel.Dock="Left" />
                    <TextBlock Text="{Binding}" />
                </DockPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Window>

MainWindow.xaml.cs

A few points to note about this class. First, it defines the Items property that is used by the ItemsSource in our xaml. Next, it initializes a new collection with some sample items in it. Finally, it sets MainWindow's DataContext to itself, enabling the ItemsSource binding to work correctly.

using System.Collections.ObjectModel;

namespace MultiSelectionSpike
{
    /// <summary>
    ///     Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        public MainWindow()
        {
            Items = new ObservableCollection<string> {"First", "Second", "Third"};
            InitializeComponent();
            DataContext = this;
        }

        public ObservableCollection<string> Items { get; }
    }
}