Search code examples
wpfdatagridcellvisibilityedit

Edit cells in DataGrid with hidden rows


In WPF I have a DataGrid with some Collapsed rows. When I edit a cell and then press enter, if the next row is collapsed then the selection doesn't move to the next visible row. Instead a dotted rectangle surrounds the cell I've just edited and typing the keyboard results in no action at all. Any idea how I can make the selection jump to the next visible row? Thank you

Example (under framework 4.0): The xaml:

<Window x:Class="WpfDataGridEdit.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"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <DataGrid AutoGenerateColumns="False" Name="dataGrid">
        <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Path=Value1}"/>
            <DataGridTextColumn Binding="{Binding Path=Value2}"/>
        </DataGrid.Columns>
        <DataGrid.RowStyle>
            <Style TargetType="{x:Type DataGridRow}">
                <Setter Property="Visibility" Value="{Binding Visibility}" />
            </Style>
        </DataGrid.RowStyle>
    </DataGrid>
</Window>

The code behind:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfDataGridEdit
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private List<Row> rows = new List<Row>();

        public MainWindow()
        {
            InitializeComponent();

            for (int i = 0; i < 10; i++)
                this.rows.Add(new Row { Value1 = i.ToString(), Value2 = "x", Visibility = i % 3 == 0 ? Visibility.Collapsed : Visibility.Visible });

            this.dataGrid.ItemsSource = this.rows;
        }
    }

    public class Row
    {
        private string value1;
        public string Value1
        {
            get { return this.value1; }
            set { this.value1 = value; }
        }

        private string value2;
        public string Value2
        {
            get { return this.value2; }
            set { this.value2 = value; }
        }

        private Visibility visibility;
        public Visibility Visibility
        {
            get { return this.visibility; }
            set { this.visibility = value; }
        }
    }
}

By editing rows and typing enter you should get stuck in the second row.


Solution

  • Any idea how I can make the selection jump to the next visible row?

    This behaviour has been reported as a bug: https://connect.microsoft.com/VisualStudio/feedback/details/526014/keyboard-navigation-doesnt-work-when-the-wpf-datagrid-has-collapsed-or-hidden-rows

    You may be able to get around it by handling the PreviewKeyDown event for the DataGrid something like this:

    private void dataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            e.Handled = true;
            DataGrid dg = sender as DataGrid;
            int index = dg.SelectedIndex;
            if (index < dg.Items.Count - 1)
            {
                dg.SelectedIndex++;
                DataGridRow nextRow = dg.ItemContainerGenerator.ContainerFromIndex(dg.SelectedIndex) as DataGridRow;
                while (nextRow != null && nextRow.Visibility == Visibility.Collapsed)
                    nextRow = dg.ItemContainerGenerator.ContainerFromIndex(dg.SelectedIndex + 1) as DataGridRow;
    
                if (nextRow != null)
                {
                    dg.SelectedItem = nextRow.DataContext;
                    dg.CurrentCell = new DataGridCellInfo(nextRow.DataContext, dg.Columns[0]);
                }
            }
        }
    }
    

    Edit: You may also want to save the index of the currently edited column by hooking up an event handler to the BeginningEdit event:

    DataGridColumn _lastEditedColumn;
    private void dataGrid_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
    {
        DataGrid dg = sender as DataGrid;
        _lastEditedColumn = dg.CurrentCell.Column;
    }
    

    ..and set the CurrentCell to this one:

    if (nextRow != null)
    {
        dg.SelectedItem = nextRow.DataContext;
        dg.CurrentCell = new DataGridCellInfo(nextRow.DataContext, _lastEditedColumn); //<---
    }