In WPF, Commands allows us to write actions in the View Models that can be bound to different controls like Button, Menu Item, etc. In this post, we'll learn to bind commands to ListBox items in WPF.
This code can be used for ListView command binding also.
Download Source Code
The source code of this project is available on GitHub.
The Problem
We can bind commands only to controls that inherit from the ButtonBase class. These controls include Button, MenuItem, etc. This means that we cannot bind commands to a ListBox, ListView, or a ListBoxItem.
The Solution
There are different methods to bind commands to a ListBox. One method is to write a custom control template for the ItemContainer and place the content of the container in an element that supports command binding.
Bind Command To ListBox / ListView
Let us now learn to bind a command to a ListView or ListBox.
1) Create a command
First, we have to create a new custom command that implements the ICommand interface. Usually, I place all custom commands in a folder called CustomCommands.
public class RelayCommand: ICommand
{
private Action<object> _handler;
private Predicate<object> _canExecute;
public RelayCommand(Action<object> handler, Predicate<object> canExecute)
{
_handler = handler;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_handler(parameter);
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
}
2) Create a ViewModel
Create a new folder called ViewModels and add the following class.
MainWindowViewModel.cs
using System.Windows;
using WpfCommandBindingOnListItem.CustomCommands;
namespace WpfCommandBindingOnListItem.ViewModels
{
public class MainWindowViewModel
{
public RelayCommand SelectCommand { get; set; }
public MainWindowViewModel()
{
SelectCommand = new RelayCommand(SelectedCommandHandler, CanExecuteSelectedCommand);
}
private void SelectedCommandHandler(object data)
{
var selectedItem = (ListBoxItem) data;
// var selectedItem = (ListViewItem) data;
MessageBox.Show($"Selected: {selectedItem.Content}");
}
private bool CanExecuteSelectedCommand(object data) => true;
}
}
Make necessary changes in the namespaces.
3) Set Data Context
Set view model class as the data context. This can be done in two ways.
Add the following line in the constructor of the window:
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
or modify the window XAML code as shown below.
<Window x:Class="WpfCommandBindingOnListItem.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:WpfCommandBindingOnListItem"
xmlns:vm="clr-namespace:WpfCommandBindingOnListItem.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<vm:MainWindowViewModel></vm:MainWindowViewModel>
</Window.DataContext>
<StackPanel>
</StackPanel>
</Window>
xmlns:vm="clr-namespace:WpfCommandBindingOnListItem.ViewModels"
. Make necessary changes in the path based on your project.4) Create a ListBox
Create a ListBox or ListView control and add a few items to it.
<ListBox>
<ListBoxItem>List Item One</ListBoxItem>
<ListBoxItem>List Item Two</ListBoxItem>
<ListBoxItem>List Item Three</ListBoxItem>
</ListBox>
5) Add a control template and bind a command
<ListBox Name="testBox">
<ListBoxItem>List Item One</ListBoxItem>
<ListBoxItem>List Item Two</ListBoxItem>
<ListBoxItem>List Item Three</ListBoxItem>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Button
Background="Transparent"
Height="20"
BorderThickness="0"
HorizontalContentAlignment="Left"
Command="{Binding RelativeSource={
RelativeSource AncestorType=ListBox},
Path=DataContext.SelectCommand}"
CommandParameter="{Binding RelativeSource={
RelativeSource AncestorType=ListBox},
Path=SelectedItem}">
<ContentPresenter></ContentPresenter>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
In this code, we are overriding the default Item Container Style to add a button to present the content so that we can bind commands to it.

Happy Coding.