Переместить фокус на следующую ячейку при нажатии клавиши Enter в WPF DataGrid?
Я хочу иметь Custom DataGrid, который может,
- Переход к следующей ячейке при нажатии клавиши Enter также, если она находится в режиме редактирования.
- Когда будет достигнут последний столбец в текущей строке, фокус должен переместиться на первую ячейку следующей строки.
- При достижении следующей ячейки, если ячейка является редактируемой, она должна автоматически стать редактируемой.
- Если ячейка содержит
ComboBox
не comboboxcolumn, combobox должен DropDownOpen.
Пожалуйста, помогите мне в этом. Я пытался с прошлых нескольких дней, создав пользовательский DataGrid и написал некоторый код в
protected override void OnPreviewKeyDown(System.Windows.Input.KeyEventArgs e)
Но я потерпел неудачу.
8 ответов
private void dg_PreviewKeyDown(object sender, KeyEventArgs e)
{
try
{
if (e.Key == Key.Enter)
{
e.Handled = true;
var cell = GetCell(dgIssuance, dgIssuance.Items.Count - 1, 2);
if (cell != null)
{
cell.IsSelected = true;
cell.Focus();
dg.BeginEdit();
}
}
}
catch (Exception ex)
{
MessageBox(ex.Message, "Error", MessageType.Error);
}
}
public static DataGridCell GetCell(DataGrid dg, int row, int column)
{
var rowContainer = GetRow(dg, row);
if (rowContainer != null)
{
var presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
if (presenter != null)
{
// try to get the cell but it may possibly be virtualized
var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
if (cell == null)
{
// now try to bring into view and retreive the cell
dg.ScrollIntoView(rowContainer, dg.Columns[column]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
}
return cell;
}
}
return null;
}
Гораздо более простая реализация. Идея состоит в том, чтобы зафиксировать событие нажатия клавиши, и если клавиша "Enter", то перейти к следующей вкладке, которая является следующей ячейкой сетки.
/// <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));
}
}
Как насчет этого решения? Отмените действие Enter клавиши, установивHandled=true
и нажмите Tab клавишу.
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);
}
}
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();
}
}
}
}
В настоящее время я написал это, и это работает для меня.
Ключевым методом является dataGrid.SetKeyboardFocusToCell. Итак, мы можем прикрепить событие KeyDown:
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);
}
В этом примере также выделен текст в следующем поле. Может быть полезно для полей, в которых большую часть времени содержимое заменяется путем ввода нового.
Важно отметить, что обычно операции после «dataGrid.SetKeyboardFocusToCell()» должны быть отправлены через диспетчер, чтобы пользовательский интерфейс мог завершить обновление. В противном случае могут произойти странные вещи.
С помощью этой схемы вы можете, например, даже вставить строку позади текущей.
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
private void dg_ExpenseItem_PreviewKeyDown(object sender, KeyEventArgs e)
{
try
{
DataGrid grid = (DataGrid)sender;
if (e.Key == Key.Enter)
{
e.Handled = true;
var cell = GetCell(grid, grid.SelectedIndex, grid.CurrentCell.Column.DisplayIndex+1);
if (cell != null)
{
cell.IsSelected = true;
cell.Focus();
grid.BeginEdit();
}
}
}
catch (Exception ex)
{
}
}
public static DataGridCell GetCell(DataGrid grid, int row, int column)
{
var rowContainer = GetRow(grid, row);
if (rowContainer != null)
{
var presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
if (presenter != null)
{
// try to get the cell but it may possibly be virtualized
var cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
if (cell == null)
{
// now try to bring into view and retreive the cell
grid.ScrollIntoView(rowContainer, grid.Columns[column]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
}
return cell;
}
}
return null;
}
static public DataGridRow GetRow(DataGrid dg, int index)
{
DataGridRow row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromIndex(index);
if (row == null)
{
// may be virtualized, bring into view and try again
dg.ScrollIntoView(dg.Items[index]);
row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromIndex(index);
}
return row;
}
static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
private void Datagrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
if (Keyboard.FocusedElement is UIElement elementWithFocus)
{
switch (dvDataGrid.CurrentCell.Column.DisplayIndex)
{
case 3
:
DataGridRow
CurrentRows =
dvDataGrid.ItemContainerGenerator.ContainerFromItem(dvDataGrid.CurrentItem) as
DataGridRow;
elementWithFocus.MoveFocus
(new TraversalRequest(FocusNavigationDirection.Next));
e.Handled = true;
break;
default:
DataGridRow CurrentRowsdea =
dvDataGrid.ItemContainerGenerator.ContainerFromItem(dvDataGrid.CurrentItem) as
DataGridRow;
elementWithFocus.MoveFocus
(new TraversalRequest(FocusNavigationDirection.Next));
e.Handled = true;
break;
}
}
}
}