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.



MainWindow.xaml:
<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:this="clr-namespace:WpfApplication3"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid x:Name="EmployeeGrid"
                  ItemsSource="{Binding Employees}"
                  AutoGenerateColumns="False"
                  IsReadOnly="True"
                  SelectionUnit="Cell">
            <DataGrid.Resources>
                <this:EqualityConverter x:Key="EqualityConverter"/>

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

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

            </DataGrid.Resources>
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
                <DataGridTextColumn Header="Age" Binding="{Binding Age}"/>
                <DataGridTemplateColumn CellStyle="{StaticResource ClickableCell}">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Button Content="Highlight"
                                    Margin="10"
                                    Click="HighlightClick"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>


MainWindow.xaml.cs:
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()
        {
            InitializeComponent();
            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; }
            set
            {
                _highlightedEmployee = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this,
                        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();
        }
    }
}

No comments:

Post a Comment