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 theAutoEditColumn
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.