'Move Focus to Next Cell on Enter Key Press in WPF DataGrid?
I want to have a Custom DataGrid which can,
- Move to next cell when Enter key is pressed also if it is in edit mode.
- When the last column in the current row is reach, the focus should move to the first cell of next row.
- On reaching to next cell, if the cell is editable, it should automatically became editable.
- If the cell contains an
ComboBoxnot comboboxcolumn, the combobox should DropDownOpen.
Please help me in this. I have been trying from the past few day by creating a Custom DataGrid and wrote some code in
protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
But I failed.
Solution 1:[1]
A much simpler implementation. The idea is to capture the keydown event and if the key is "Enter", then move to the next tab which is next cell of the grid.
/// <summary>
/// On Enter Key, it tabs to into next cell.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DataGrid_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
var uiElement = e.OriginalSource as UIElement;
if (e.Key == Key.Enter && uiElement != null)
{
e.Handled = true;
uiElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
Solution 2:[2]
How about this solution? Cancel the action of the Enter key by setting Handled=true and press the Tab key.
public Constructor()
{
InitializeComponent();
this.SampleDataGrid.PreviewKeyDown += MoveCellOnEnterKey;
}
private void MoveCellOnEnterKey(object sender, KeyEventArgs e)
{
if(e.Key == Key.Enter)
{
// Cancel [Enter] key event.
e.Handled = true;
// Press [Tab] key programatically.
var tabKeyEvent = new KeyEventArgs(
e.KeyboardDevice, e.InputSource, e.Timestamp, Key.Tab);
tabKeyEvent.RoutedEvent = Keyboard.KeyDownEvent;
InputManager.Current.ProcessInput(tabKeyEvent);
}
}
Solution 3:[3]
public class DataGrid : System.Windows.Controls.DataGrid
{
private void PressKey(Key key)
{
KeyEventArgs args = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, key);
args.RoutedEvent = Keyboard.KeyDownEvent;
InputManager.Current.ProcessInput(args);
}
protected override void OnCurrentCellChanged(EventArgs e)
{
if (this.CurrentCell.Column != null)
if (this.CurrentCell.Column.DisplayIndex == 2)
{
if (this.CurrentCell.Item.ToString() == "--End Of List--")
{
this.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
}
}
else if (this.CurrentCell.Column != null && this.CurrentCell.Column.DisplayIndex == this.Columns.Count() - 1)
{
PressKey(Key.Return);
DataGridCell cell = DataGridHelper.GetCell(this.CurrentCell);
int index = DataGridHelper.GetRowIndex(cell);
DataGridRow dgrow = (DataGridRow)this.ItemContainerGenerator.ContainerFromItem(this.Items[index]);
dgrow.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
DataGridRow rowContainer = (DataGridRow)this.ItemContainerGenerator.ContainerFromItem(this.CurrentItem);
if (rowContainer != null)
{
int columnIndex = this.Columns.IndexOf(this.CurrentColumn);
DataGridCellsPresenter presenter = UIHelper.GetVisualChild<DataGridCellsPresenter>(rowContainer);
if (columnIndex == 0)
{
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
request.Wrapped = true;
cell.MoveFocus(request);
BeginEdit();
PressKey(Key.Down);
}
else
{
CommitEdit();
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
TraversalRequest request = new TraversalRequest(FocusNavigationDirection.Next);
request.Wrapped = true;
cell.MoveFocus(request);
}
this.SelectedItem = this.CurrentItem;
e.Handled = true;
this.UpdateLayout();
}
}
}
}
For the time being, I have written this and its working for me.
Solution 4:[4]
Public Sub SendKey(ByVal key As Key)
Dim args As New KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, key)
args.RoutedEvent = Keyboard.KeyDownEvent
InputManager.Current.ProcessInput(args)
End Sub
Private Sub dataGrid_PreviewKeyDown(sender As Object, e As KeyEventArgs) Handles dataGrid.PreviewKeyDown
Dim i As UIElement = e.OriginalSource
Dim DG As DataGrid = sender
If (e.Key = Key.Enter Or e.Key = Key.Return) AndAlso i IsNot Nothing Then
MyBase.OnKeyDown(e)
DG.CommitEdit()
SendKey(Key.Tab)
e.Handled = True
End If
End Sub
Solution 5:[5]
The key method is 'dataGrid.SetKeyboardFocusToCell'. So we can attach the KeyDown event:
private void dataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
DataGridTemplateColumn col = (DataGridTemplateColumn)dataGrid.CurrentColumn;
if (col != null)
{
switch (col.SortMemberPath)
{
case "From":
if (e.Key == Key.Enter && Keyboard.Modifiers == ModifierKeys.None) // Pure Enter
{
e.Handled = true;
int columnIndex = dataGrid.GetColumnIndex(colTo);
DataGridRow currentRow = dataGrid.GetRow(dataGrid.CurrentItem);
dataGrid.SetKeyboardFocusToCell(dataGrid.CurrentItem, columnIndex);
Dispatcher.Invoke(() =>
{
GridTimeSpanBox timeSpanBox = VisualTree.FindChild<GridTimeSpanBox>(currentRow, tsb => tsb.Name == "tsbTo", true);
timeSpanBox.SelectAll();
}, System.Windows.Threading.DispatcherPriority.ContextIdle);
}
break;
}
} // col != null
}
/// <summary>
/// Get the row container that holds the 'item'
/// </summary>
public DataGridRow GetRow(object item)
{
return (DataGridRow)ItemContainerGenerator.ContainerFromItem(item);
}
/// <summary>
/// Gets the index of a 'DataGridColum' or 'DataGridTemplateColumn' in the 'Columns' list. This doesn't change if the user
/// reorders the columns.
/// </summary>
public int GetColumnIndex(DataGridColumn column)
{
return this.Columns.IndexOf(column);
}
In this example the text in the following box is selected too. Can be useful for fields where most of the time the content is replaced by typing a new one.
Important to note that generally operations after 'dataGrid.SetKeyboardFocusToCell()' must be sent over the dispatcher to allow the UI to finish updating. Otherwise strange things can happen.
With this scheme you could for example even insert a row behind the current one.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Hunter |
| Solution 2 | Anton Menshov |
| Solution 3 | akjoshi |
| Solution 4 | Hassan H Daboor |
| Solution 5 |
