Home > Blockchain >  WPF - How to correctly trigger edit mode in a DataGrid without a mouse click
WPF - How to correctly trigger edit mode in a DataGrid without a mouse click

Time:03-16

I am trying to set up a datagrid such that when the grid receives focus the current/selected row goes into edit mode for a specified cell. Unfortunately the row still needs a mouse click for the cell to enter edit mode.

The grid gets focus via datatrigger bound to a boolean, and this correctly triggers the GotFocus method in the code behind, which is supposed to specify the current cell and BeginEdit.

XML code:

<DataGrid x:Name="SampleAliquotsDataGrid" ItemsSource="{Binding Source={StaticResource cvsSampleModels}}" 
                  CurrentItem="{Binding CurrentSampleAliquot, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"                   
                  SelectedItem="{Binding CurrentSampleAliquot, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" 
                  AutoGenerateColumns="False" Grid.Row="1" SelectionUnit="FullRow"
                  GotFocus="SampleAliquotsDataGrid_GotFocus"
                  >
            <DataGrid.Style>
                <Style>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding DataGridHasFocus}" Value="True">
                            <Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=SampleAliquotsDataGrid}"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.Style>
            <DataGrid.Columns>
                <DataGridTemplateColumn Header="RecordID" Width="Auto" IsReadOnly="True">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Label Content="{Binding DBRecordID}" HorizontalContentAlignment="Right"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTextColumn x:Name="MeasuredResult" Binding="{Binding MeasuredResult, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="MeasuredResult" 
                                        FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"/>
               </DataGrid.Columns>                 
</DataGrid>

The cell to edit is the DataGridTextColumn "MeasuredResult".

the gotfocus code behind:

private void SampleAliquotsDataGrid_GotFocus(object sender, RoutedEventArgs e)
        {
            DataGrid grid = sender as DataGrid;
            
            grid.CurrentCell = new DataGridCellInfo(grid.SelectedItem, grid.Columns[1]);
            
            grid.BeginEdit();
            e.Handled = true;
        }

I've tried changing how the currentcell gets set, re-focusing on the grid before calling BeginEdit, changing the selection unit to Cell instead of fullrow.

Hopefully the solution isn't too complicated, my skills with WPF aren't sophisticated.

CodePudding user response:

I propose the behavior bellow:

  • When a cell is clicked on the selected row, the behavior of the DataGrid is unchanged.
  • When a cell is clicked on a not select row, its row is selected and the cell in the AutoEditColumn is set in edition mode.
  • When the DataGrid gain focus from keyboard (tab navigation), the cell in the AutoEditColumn and selected row is set in edition mode.

To achieve this, one can listen on the CurrentCellChanged event.
Here a helper class that implement the described behavior :

private class AutoEditColumnHelper : IDisposable
{
    private readonly DataGrid _dataGrid;
    private readonly int _columnIndex;

    private object? _lastItem;

    public AutoEditColumnHelper(DataGrid dataGrid, int columnIndex)
    {
        _dataGrid = dataGrid;
        _columnIndex = columnIndex;

        _dataGrid.CurrentCellChanged  = OnCurrentCellChanged;
    }

    public void Dispose()
    {
        _dataGrid.CurrentCellChanged -= OnCurrentCellChanged;
    }

    private void OnCurrentCellChanged(object? sender, EventArgs e)
    {
        var currentCell = _dataGrid.CurrentCell;

        if (!currentCell.IsValid || Equals(currentCell.Item, _lastItem))
            return;

        var autoEditColumn = GetAutoEditColumn();
        if (autoEditColumn is null)
            return;

        _dataGrid.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, () =>
        {
            _lastItem = _dataGrid.SelectedItem;
            _dataGrid.CurrentCell = new DataGridCellInfo(_lastItem, autoEditColumn);
            _dataGrid.BeginEdit();
        });
    }

    private DataGridColumn? GetAutoEditColumn()
    {
        return _columnIndex < 0 || _columnIndex > _dataGrid.Columns.Count ? null : _dataGrid.Columns[_columnIndex];
    }
}

To use this class, AttachedProperty are the recommended way.

First, we declare a public AutoEditColumn attached property that store the AutoEditColumn index in an int.
Then, we declare a private AutoEditColumnHelper attached property that store an instance of the AutoEditColumnHelper below for each DataGrid that have a AutoEditColumn property.

public static readonly DependencyProperty AutoEditColumnProperty = DependencyProperty.RegisterAttached("AutoEditColumn", typeof(int), typeof(DataGridEx), new PropertyMetadata(default(int), AutoEditColumnChangedCallback));

public static void SetAutoEditColumn(DependencyObject element, int value)
{
    element.SetValue(AutoEditColumnProperty, value);
}

public static int GetAutoEditColumn(DependencyObject element)
{
    return (int)element.GetValue(AutoEditColumnProperty);
}

private static void AutoEditColumnChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (d is not DataGrid dataGrid)
        return;

    GetAutoEditColumnHelper(dataGrid)?.Dispose();

    if (e.NewValue is int columnIndex)
    {
        SetAutoEditColumnHelper(d, new AutoEditColumnHelper(dataGrid, columnIndex));
    }
    else
    {
        d.ClearValue(AutoEditColumnHelperProperty);
    }
}


private static readonly DependencyProperty AutoEditColumnHelperProperty = DependencyProperty.RegisterAttached("AutoEditColumnHelper", typeof(AutoEditColumnHelper), typeof(DataGridEx), new PropertyMetadata(default(AutoEditColumnHelper)));

private static void SetAutoEditColumnHelper(DependencyObject element, AutoEditColumnHelper value)
{
    element.SetValue(AutoEditColumnHelperProperty, value);
}

private static AutoEditColumnHelper? GetAutoEditColumnHelper(DependencyObject element)
{
    return element.GetValue(AutoEditColumnHelperProperty) as AutoEditColumnHelper;
}

Finally the AutoEditColumn attached property is used in the xaml code:

<DataGrid AutoGenerateColumns="False"
          so71475662:DataGridEx.AutoEditColumn="1"
          ItemsSource="{Binding Path=Items}"
          SelectionMode="Single"
          SelectionUnit="FullRow">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Label}" Header="Label" />
        <DataGridTextColumn Binding="{Binding Value}" Header="Value" />
    </DataGrid.Columns>
</DataGrid>

Full working example is available here.

  • Related