madbadger
madbadger

Reputation: 644

WPF DataGrid full row selection

I'm using WPF and .NET 4.0. Recently in one of my programs I switched from using ListView with GridView to DataGrid.

I want to be able to select and highlight the whole row like I was able to do in ListView.

In ListView, when I click on the empty space right from the last column, I'm still able to select the row. The whole row is highlighted, not only the cells.

In DataGrid however, after setting SelectionMode="Single" and SelectionUnit="FullRow", the row is selectable only when I click on any cell in it, not in the empty space right to the last column.

How can I use the highlighting behavior from ListView here?

Upvotes: 14

Views: 14552

Answers (4)

Mohammed A. Fadil
Mohammed A. Fadil

Reputation: 9377

There are two solutions:

  1. Set the width of the last column in the DataGrid to Width="*".
  2. The second solution is a workaround. Add an additional empty column after the last column (i.e. neither setting its Header nor Binding properties) and set its width to Width="*"

I personally prefer the first solution; it's cleaner than the second one.

Upvotes: 12

Lino
Lino

Reputation: 11

Some of these solutions would not work in my case. So what I did is re-route the event to the last cell of the row (which is the closest to the click). You can add that to a Behavior or in a code-behind:

_dataGrid.MouseLeftButtonDown += (sender, args) =>
{
        //If click was on row border, reroute to latest cell of row
        if (!(args.OriginalSource is Border border) || !(border.Child is SelectiveScrollingGrid grid)) return;

        var cellsPresenter = grid.Children.OfType<DataGridCellsPresenter>().FirstOrDefault();
        if (cellsPresenter == null || cellsPresenter.Items.Count < 1) return;

        var lastCell = (DataGridCell)cellsPresenter.ItemContainerGenerator.ContainerFromIndex(cellsPresenter.Items.Count - 1);
        lastCell.RaiseEvent(args);
};

Upvotes: 1

Bosko Kukanjac
Bosko Kukanjac

Reputation: 21

Based on previous comment from Aleksey L., here is the solution with DataGridBehavior class with FullRowSelect dependency property.

Behavior will automatically attach MouseDown event handler. Also, it will set "SelectionMode = DataGridSelectionMode.Single" so it will work properly with DataContext and SelectedItem binding.

XAML

<UserControl x:Class="WpfDemo.Views.Default"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:b="clr-namespace:WpfDemo.Behaviors"
         mc:Ignorable="d" 
         d:DesignHeight="300">

        <DataGrid b:DataGridBehavior.FullRowSelect="True">
              ...
        </DataGrid>             

DataGridBehavior class

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfDemo.Behaviors
{
    /// <summary>
    /// Extends <see cref="DataGrid"/> element functionality.
    /// </summary>
    public static class DataGridBehavior
    {
        #region - Dependency properties -

        /// <summary>
        /// Forces row selection on empty cell, full row select.
        /// </summary>
        public static readonly DependencyProperty FullRowSelectProperty = DependencyProperty.RegisterAttached("FullRowSelect",
            typeof(bool),
            typeof(DataGridBehavior),
            new UIPropertyMetadata(false, OnFullRowSelectChanged));

        #endregion

        #region - Public methods -

        /// <summary>
        /// Gets property value.
        /// </summary>
        /// <param name="grid">Frame.</param>
        /// <returns>True if row should be selected when clicked outside of the last cell, otherwise false.</returns>
        public static bool GetFullRowSelect(DataGrid grid)
        {
            return (bool)grid.GetValue(FullRowSelectProperty);
        }

        /// <summary>
        /// Sets property value.
        /// </summary>
        /// <param name="grid">Frame.</param>
        /// <param name="value">Value indicating whether row should be selected when clicked outside of the last cell.</param>
        public static void SetFullRowSelect(DataGrid grid, bool value)
        {
            grid.SetValue(FullRowSelectProperty, value);
        }

        #endregion

        #region - Private methods -

        /// <summary>
        /// Occurs when FullRowSelectProperty has changed.
        /// </summary>
        /// <param name="depObj">Dependency object.</param>
        /// <param name="e">Event arguments.</param>
        private static void OnFullRowSelectChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
        {
            DataGrid grid = depObj as DataGrid;
            if (grid == null)
                return;

            if (e.NewValue is bool == false)
            {
                grid.MouseDown -= OnMouseDown;

                return;
            }

            if ((bool)e.NewValue)
            {
                grid.SelectionMode = DataGridSelectionMode.Single;

                grid.MouseDown += OnMouseDown;
            }
        }

        private static void OnMouseDown(object sender, MouseButtonEventArgs e)
        {
            var dependencyObject = (DependencyObject)e.OriginalSource;

            while ((dependencyObject != null) && !(dependencyObject is DataGridRow))
            {
                dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
            }

            var row = dependencyObject as DataGridRow;
            if (row == null)
            {
                return;
            }

            row.IsSelected = true;
        }

        #endregion
    }
}

Upvotes: 2

Aleksey L.
Aleksey L.

Reputation: 37986

There is one more solution if you can use code behind in your project. You can handle mouse down event of datagrid and programmatically select the clicked row:

 private void SomeGridMouseDown(object sender, MouseButtonEventArgs e)
    {
        var dependencyObject = (DependencyObject)e.OriginalSource;

        //get clicked row from Visual Tree
        while ((dependencyObject != null) && !(dependencyObject is DataGridRow))
        {
            dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
        }

        var row = dependencyObject as DataGridRow;
        if (row == null)
        {
            return;
        }

        row.IsSelected = true;
    }

Upvotes: 2

Related Questions