How to perform Single click checkbox selection in WPF DataGrid?

WpfDatagridWpfdatagrid

Wpf Problem Overview


I have a DataGrid with first column as text column and second column as CheckBox column. What I want is, if I click the check box. It should get checked.

But, it takes two click to get selected, for first click the cell is getting selected, for the second clicks the check box is getting checked. How to make the check box to get checked/unchecked with a single click.

I'm using WPF 4.0. Columns in the DataGrid are AutoGenerated.

Wpf Solutions


Solution 1 - Wpf

For single click DataGrid checkbox you can just put regular checkbox control inside DataGridTemplateColumn and set UpdateSourceTrigger=PropertyChanged.

<DataGridTemplateColumn.CellTemplate>
	<DataTemplate>
		<CheckBox IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
	</DataTemplate>
</DataGridTemplateColumn.CellTemplate>

Solution 2 - Wpf

I solved this with the following Style:

<Style TargetType="DataGridCell">
     <Style.Triggers>
         <Trigger Property="IsMouseOver" Value="True">
             <Setter Property="IsEditing" Value="True" />
         </Trigger>
     </Style.Triggers>
 </Style>

It's of course possible to adapt this further for specific columns ...

Solution 3 - Wpf

First of, I know this is a pretty old question but I still thought I'd try and answer it.

I had the same problem a couple of days ago and came across a surprisingly short solution for it (see this blog). Basically, all you need to do is replace the DataGridCheckBoxColumn definition in your XAML with the following:

<DataGridTemplateColumn Header="MyCheckBoxColumnHeader">
	<DataGridTemplateColumn.CellTemplate>
		<DataTemplate>
			<CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding Path=MyViewModelProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
		</DataTemplate>
	</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

The upside of this solution is obvious - it's XAML-only; thus it effectively refrains your from burdening your code-behind with additional UI logic.

Solution 4 - Wpf

To make Konstantin Salavatov's answer work with AutoGenerateColumns, add an event handler to the DataGrid's AutoGeneratingColumn with the following code:

if (e.Column is DataGridCheckBoxColumn && !e.Column.IsReadOnly)
{
	var checkboxFactory = new FrameworkElementFactory(typeof(CheckBox));
	checkboxFactory.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Center);
	checkboxFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
	checkboxFactory.SetBinding(ToggleButton.IsCheckedProperty, new Binding(e.PropertyName) { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });

	e.Column = new DataGridTemplateColumn
		{
			Header = e.Column.Header,
			CellTemplate = new DataTemplate { VisualTree = checkboxFactory },
			SortMemberPath = e.Column.SortMemberPath
		};
}

This will make all of DataGrid's auto-generated checkbox columns be "single click" editable.

Solution 5 - Wpf

Based on blog referenced in Goblin's answer, but modified to work in .NET 4.0 and with Row-Selection Mode.

Notice that it also speeds up DataGridComboBoxColumn editing - by entering edit mode and displaying dropdown on single click or text input.

XAML:

        <Style TargetType="{x:Type DataGridCell}">
            <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
            <EventSetter Event="PreviewTextInput" Handler="DataGridCell_PreviewTextInput" />
        </Style>

Code-behind:

    private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        GridColumnFastEdit(cell, e);
    }

    private void DataGridCell_PreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        GridColumnFastEdit(cell, e);
    }

    private static void GridColumnFastEdit(DataGridCell cell, RoutedEventArgs e)
    {
        if (cell == null || cell.IsEditing || cell.IsReadOnly)
            return;

        DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
        if (dataGrid == null)
            return;

        if (!cell.IsFocused)
        {
            cell.Focus();
        }

        if (cell.Content is CheckBox)
        {
            if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
            {
                if (!cell.IsSelected)
                    cell.IsSelected = true;
            }
            else
            {
                DataGridRow row = FindVisualParent<DataGridRow>(cell);
                if (row != null && !row.IsSelected)
                {
                    row.IsSelected = true;
                }
            }
        }
        else
        {
            ComboBox cb = cell.Content as ComboBox;
            if (cb != null)
            {
                //DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
                dataGrid.BeginEdit(e);
                cell.Dispatcher.Invoke(
                 DispatcherPriority.Background,
                 new Action(delegate { }));
                cb.IsDropDownOpen = true;
            }
        }
    }


    private static T FindVisualParent<T>(UIElement element) where T : UIElement
    {
        UIElement parent = element;
        while (parent != null)
        {
            T correctlyTyped = parent as T;
            if (correctlyTyped != null)
            {
                return correctlyTyped;
            }

            parent = VisualTreeHelper.GetParent(parent) as UIElement;
        }
        return null;
    }

Solution 6 - Wpf

I've tried these suggestions, and plenty of others I've found on other sites, but none of them quite worked for me. In the end, I created the following solution.

I've created my own DataGrid-inherited control, and simply added this code to it:

public class DataGridWithNavigation : Microsoft.Windows.Controls.DataGrid
{
	public DataGridWithNavigation()
	{
		EventManager.RegisterClassHandler(typeof(DataGridCell), 
			DataGridCell.PreviewMouseLeftButtonDownEvent,
			new RoutedEventHandler(this.OnPreviewMouseLeftButtonDown));
	}


	private void OnPreviewMouseLeftButtonDown(object sender, RoutedEventArgs e)
	{
		DataGridCell cell = sender as DataGridCell;
		if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
		{
		  DependencyObject obj = FindFirstControlInChildren(cell, "CheckBox");
			if (obj != null)
			{
				System.Windows.Controls.CheckBox cb = (System.Windows.Controls.CheckBox)obj;
				cb.Focus();
				cb.IsChecked = !cb.IsChecked;
			}
		}
	}

	public DependencyObject FindFirstControlInChildren(DependencyObject obj, string controlType)
	{
		if (obj == null)
			return null;

		// Get a list of all occurrences of a particular type of control (eg "CheckBox") 
		IEnumerable<DependencyObject> ctrls = FindInVisualTreeDown(obj, controlType);
		if (ctrls.Count() == 0)
			return null;

		return ctrls.First();
	}

	public IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, string type)
	{
		if (obj != null)
		{
			if (obj.GetType().ToString().EndsWith(type))
			{
				yield return obj;
			}

			for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
			{
				foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type))
				{
					if (child != null)
					{
						yield return child;
					}
				}
			}
		}
		yield break;
	}
}

What does all this do ?

Well, each time we click on any cell in our DataGrid, we see if the cell contains a CheckBox control within it. If it does, then we'll set the focus to that CheckBox and toggle it's value.

This seems to work for me, and is a nice, easily reusable solution.

It is disappointing that we need to write code to do this though. The explanation that the first mouse click (on a DataGrid's CheckBox) is "ignored" as WPF uses it to put the row into Edit mode might sound logical, but in the real-world, this goes against the way every real application works.

If a user sees a checkbox on their screen, they should be able to click on it once to tick/untick it. End of story.

Solution 7 - Wpf

There is a much simpler solution here.

          <DataGridTemplateColumn MinWidth="20" >
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        </Grid>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

If you use DataGridCheckBoxColumn to implement, first click is to focus, second click is to check.

But using DataGridTemplateColumn to implement needs one click only.

The difference of using DataGridComboboxBoxColumn and implementation by DataGridTemplateColumn is also similar.

Solution 8 - Wpf

Base on Jim Adorno answer and comments on his post, this is solution with MultiTrigger:

<Style TargetType="DataGridCell">
  <Style.Triggers>
    <MultiTrigger>
      <MultiTrigger.Conditions>
	<Condition Property="IsReadOnly" Value="False" />
	<Condition Property="IsMouseOver" Value="True" />
      </MultiTrigger.Conditions>
      <Setter Property="IsEditing" Value="True" />
    </MultiTrigger>
  </Style.Triggers>
</Style>

Solution 9 - Wpf

I solved with this:

<DataGridTemplateColumn>
	<DataGridTemplateColumn.CellTemplate>
		<DataTemplate>
			<Viewbox Height="25">
				<CheckBox IsChecked="{Binding TheProperty, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
			</Viewbox>
		</DataTemplate>
	</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

The checkbox active on single click!

Solution 10 - Wpf

Yet another simple solution is to add this style to your DataGridColumn.The body of your style can be empty.

<DataGridCheckBoxColumn>
     <DataGridCheckBoxColumn.ElementStyle>
          <Style TargetType="CheckBox">
           </Style>
     </DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>

Solution 11 - Wpf

<Style x:Key="StilCelula" TargetType="DataGridCell"> 
<Style.Triggers>
 <Trigger Property="IsMouseOver" Value="True">
   <Setter Property="IsEditing" 
     Value="{Binding RelativeSource={x:Static RelativeSource.Self}, 
     Converter={StaticResource CheckBoxColumnToEditingConvertor}}" />
 </Trigger>
</Style.Triggers>
<Style>

Imports System.Globalization
Public Class CheckBoxColumnToEditingConvertor
    Implements IValueConverter
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.Convert
        Try

            Return TypeOf TryCast(value, DataGridCell).Column Is DataGridCheckBoxColumn
        Catch ex As Exception
            Return Visibility.Collapsed
        End Try
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function
End Class

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPrince AshitakaView Question on Stackoverflow
Solution 1 - WpfKonstantin SalavatovView Answer on Stackoverflow
Solution 2 - WpfJim AdornoView Answer on Stackoverflow
Solution 3 - WpfPriidu NeemreView Answer on Stackoverflow
Solution 4 - WpfAllon GuralnekView Answer on Stackoverflow
Solution 5 - WpfsurfenView Answer on Stackoverflow
Solution 6 - WpfMike GledhillView Answer on Stackoverflow
Solution 7 - WpfWeidian HuangView Answer on Stackoverflow
Solution 8 - WpfRafal SpacjerView Answer on Stackoverflow
Solution 9 - WpfDarlan DieterichView Answer on Stackoverflow
Solution 10 - WpfAmirHossein RezaeiView Answer on Stackoverflow
Solution 11 - WpfTotPeRoView Answer on Stackoverflow