How To Bind Command To ListBox Item in WPF
WPF How To .NET

How To Bind Command To ListBox Item in WPF

Editorial Team
Editorial Team

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.

GitHub - mishelshaji/WPF-Command-Binding-On-ListBoxItem
Contribute to mishelshaji/WPF-Command-Binding-On-ListBoxItem development by creating an account 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>
👉
Don't forget to add the XML namespace 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.

👉
If you are using a ListView instead, replace all references to ListBox with ListView and ListBoxItem with ListViewItem respectively.

Happy Coding.



Join the conversation.