Thursday, December 6, 2012

WPF DataGrid with custom selection logic

I have a WPF DataGrid with a few text columns and a button column. When a text cell is clicked nothing should happen, when a button is clicked the row of the clicked button should be highlighted.

  • SelectionUnit = Cell is needed to don't mess things up when the mouse button is pressed on the Highlight button and depressed outside.
  • Focusable = False is needed to forbid tabbing into the text cells.
  • IsHitTestVisible = False is needed to forbid selecting a text cell with the mouse.
  • Background = Null is needed to forbid selecting the button cell when the empty space around the button is clicked.
  • IsHitTestVisible = True is needed for the button to be clickable, because if the cell isn't clickable, the button won't be either.
  • MultiBinding is needed because in the existing codebase the highlighting wasn't handled with a property on the Employee class.

<Window x:Class="WpfApplication3.MainWindow"
        Title="MainWindow" Height="350" Width="525">
        <DataGrid x:Name="EmployeeGrid"
                  ItemsSource="{Binding Employees}"
                <this:EqualityConverter x:Key="EqualityConverter"/>

                <Style TargetType="DataGridCell">
                    <Setter Property="Template">
                            <ControlTemplate TargetType="DataGridCell">
                                <Border Background="{TemplateBinding Background}">
                    <Setter Property="Focusable" Value="False"/>
                    <Setter Property="IsHitTestVisible" Value="False"/>
                    <Setter Property="Background" Value="{x:Null}"/>
                        <DataTrigger Value="True">
                                <MultiBinding Converter="{StaticResource EqualityConverter}">
                                    <Binding ElementName="EmployeeGrid"
                            <Setter Property="Background" Value="LightBlue"/>
                            <Setter Property="Foreground" Value="White"/>

                <Style x:Key="ClickableCell"
                       BasedOn="{StaticResource {x:Type DataGridCell}}">
                    <Setter Property="IsHitTestVisible" Value="True"/>

                <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
                <DataGridTextColumn Header="Age" Binding="{Binding Age}"/>
                <DataGridTemplateColumn CellStyle="{StaticResource ClickableCell}">
                            <Button Content="Highlight"

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApplication3
    public partial class MainWindow : Window
        public MainWindow()
            var employees = new List<Employee>
                new Employee { Name = "aa", Age = 10 },
                new Employee { Name = "bb", Age = 20 }
            DataContext = new Company
                Employees = employees,
                HighlightedEmployee = employees[1]

        void HighlightClick(object sender, RoutedEventArgs e)
            var company = DataContext as Company;
            var button = e.OriginalSource as Button;
            company.HighlightedEmployee = button.DataContext as Employee;

    public class Company : INotifyPropertyChanged
        Employee _highlightedEmployee;
        public List<Employee> Employees { get; set; }
        public event PropertyChangedEventHandler PropertyChanged;

        public Employee HighlightedEmployee
            get { return _highlightedEmployee; }
                _highlightedEmployee = value;
                if (PropertyChanged != null)
                        new PropertyChangedEventArgs("HighlightedEmployee"));

    public class Employee
        public string Name { get; set; }
        public int Age { get; set; }

    public class EqualityConverter : IMultiValueConverter
        public object Convert(
            object[] values,
            Type targetType,
            object parameter,
            CultureInfo culture)
            return values[0] == values[1];

        public object[] ConvertBack(
            object value,
            Type[] targetTypes,
            object parameter,
            CultureInfo culture)
            throw new NotImplementedException();