#c# #wpf #datagrid #key-bindings
#c# #wpf #сетка данных #привязки клавиш
Вопрос:
Еще один вопрос о привязках клавиш datagrid
У меня есть datagrid. Для режима выбора установлено значение FullRow и KeyboardNavigation.TabNavigation=»Once», который, как я надеялся, даст желаемый результат, но этого не происходит.
При нажатии клавиши tab, когда сетка данных находится в фокусе, она будет табулировать каждый столбец в таблице один за другим. Итак, если я перейду в таблицу с 4 столбцами, мне придется нажать tab 4 раза, чтобы перейти к следующему tabindex.
Я хочу, чтобы клавиша tab выводила вкладку прямо из datagrid при первом нажатии и фокусировалась на следующем tabindex … если это имеет смысл.
Я попытался переопределить клавишу tab в обработчике событий keydown следующим образом.
class BetterDataGrid : DataGrid
{
..............
protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e)
{
..............
if (e.Key == Key.Tab)
{
Console.WriteLine("TAB");
MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
.........
}
Он записывает «TAB» в консоль, но вкладка по-прежнему сохраняет поведение по умолчанию. Не уверен, что это правильный путь для перехода к следующему tabindex, но тогда это должно заставить клавишу tab ничего не делать, кроме записи в консоль или вызвать исключение.
Заставляет меня думать, что невозможно переопределить поведение клавиши табуляции.
Надеюсь на какой-нибудь полезный ввод.
Как всегда, заранее благодарю.
Ответ №1:
Я хотел это для своего программного обеспечения для бизнеса, и единственный способ, который я нашел для решения этой проблемы, — это с помощью codebehind, используя события PreviewKeyDown, GotKeyboardFocus и LostKeyboardFocus сетки данных. Я поместил эти обработчики событий в декоратор WPF, чтобы избежать повторения этого для каждой отдельной DataGrid. Вероятно, можно было бы создать подкласс DataGrid, но я этого не пробовал.
Код для обработчиков выглядит следующим образом (DataGrid — это x:Name=»сетка» для этого примера кода):
private IInputElement lastDataGridFocus = null;
private int selectedcolumnindex = 0;
void grid_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (grid.Items.Count > 0 amp;amp; (e.NewFocus is DataGrid || (e.NewFocus is DataGridCell amp;amp; !(e.OldFocus is DataGridCell))))
{
DataGridCell cell = null;
if (lastDataGridFocus != null)
{
FocusManager.SetFocusedElement(grid, lastDataGridFocus);
lastDataGridFocus = null;
e.Handled = true;
return;
}
if (grid.SelectedCells.Count == 0)
{
DataGridRow rowContainer = (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(0);
if (rowContainer != null)
{
DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex((selectedcolumnindex < 0) ? 0 : selectedcolumnindex);
}
}
else
{
DataGridCellInfo selectedDataGridCellInfo = (grid.SelectedCells[0] as DataGridCellInfo?).Value;
DataGridRow rowContainer = (DataGridRow)grid.ItemContainerGenerator.ContainerFromItem(selectedDataGridCellInfo.Item);
if (rowContainer != null)
{
DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex((selectedcolumnindex < 0) ? 0 : selectedcolumnindex);
}
}
if (null != cell)
{
FocusManager.SetFocusedElement(grid, cell as IInputElement);
e.Handled = true;
}
}
}
void grid_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
if (!(e.NewFocus is DataGridCell))
{
if (grid.CurrentCell != null)
{
selectedcolumnindex = grid.Columns.IndexOf(grid.CurrentCell.Column);
}
}
}
void grid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.Modifiers == ModifierKeys.Shift amp;amp; e.Key == Key.Tab)
{
lastDataGridFocus = Keyboard.FocusedElement;
grid.MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
e.Handled = true;
}
else if (Keyboard.Modifiers == ModifierKeys.None amp;amp; e.Key == Key.Tab)
{
lastDataGridFocus = Keyboard.FocusedElement;
grid.MoveFocus(new TraversalRequest(FocusNavigationDirection.Last));
(Keyboard.FocusedElement as FrameworkElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
e.Handled = true;
}
}
С помощью этого кода вы можете перемещаться внутри сетки с помощью клавиш курсора, а клавиши tab и shift-tab выводят вас из сетки данных. Если вы выводите табуляцию из сетки и возвращаетесь к сетке, вы также попадаете в ту же ячейку, из которой вышли. Это то, чего хотим мои пользователи и я, и это, ИМХО, то, что элемент управления DataGrid должен предоставлять в качестве поведения по умолчанию.
Комментарии:
1. Извините за позднее принятие, у меня не было возможности протестировать это, но это хорошо. Спасибо
2. Использование
FocusManager.SetFocusedElement
вызывает две проблемы: 1. он повторно отправит ‘ t’ в качестве входных данных в выбранную ячейку 2. иногда не удается установить фокус клавиатуры и / или выбрать следующую ячейку. ИспользованиеDispatcher.BeginInvoke(new Action(() => cell.Focus()));
должно устранить обе проблемы.
Ответ №2:
Я также искал такое поведение. Хотя решение, предложенное Guge, было хорошим началом, мне не понравилось ни то, как оно сохраняет ранее сохраненный элемент, ни его общая сложность. Хуже всего то, что я просто не мог заставить его стабильно работать должным образом, независимо от того, насколько сильно я его настраивал. В конце концов, я решил написать свое собственное решение с нуля. Нестандартным мышлением (буквально) Я придумал другое, более простое решение.
В файле XAML создайте пустой элемент управления до и после вашей DataGrid следующим образом:
<DockPanel>
<Control IsTabStop="False" x:Name="PreControl" />
<DataGrid PreviewKeyDown="DataGrid_PreviewKeyDown">...</DataGrid>
<Control IsTabStop="False" x:Name="PostControl" />
</DockPanel>
Затем в коде, лежащем в основе, добавьте функцию для события PreviewKeyDown сетки данных, например:
private void DataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (Keyboard.Modifiers == ModifierKeys.Shift amp;amp; e.Key == Key.Tab)
{
PreControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
e.Handled = true;
}
else if (Keyboard.Modifiers == ModifierKeys.None amp;amp; e.Key == Key.Tab)
{
PostControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
e.Handled = true;
}
else if (new[] { Key.Up, Key.Down, Key.Left, Key.Right }.Contains(e.Key))
{
var grid = (DataGrid)sender;
grid.CurrentCell = new DataGridCellInfo(grid.SelectedItem, grid.CurrentColumn);
}
}
Первое if и else-if переопределяют поведение табуляции по умолчанию, вводя табуляцию из пустых элементов управления, а не из datagrid. Следующий оператор else-if обновляет текущую ячейку перед перемещением с помощью клавиш со стрелками. Иногда текущая ячейка не синхронизируется с выбранной ячейкой при переключении фокуса в сетке и за ее пределами. Это проблема с ранее предложенным решением, а также с этим, и я не нашел способа ее исправить, но, делая это, я могу убедиться, что при навигации с помощью клавиш со стрелками она перемещается относительно выбранной ячейки, а не текущей ячейки.
Некоторые оговорки к этому подходу:
- Это не будет хорошо работать, если разрешить выбор нескольких строк
- Как упоминалось ранее, иногда текущая ячейка может отличаться от выбранной ячейки. Это может вызвать визуальные проблемы, но не влияет на выбранный элемент или навигацию.
- Я тщательно протестировал это только с сеткой данных, в которой есть полный выбор строк. Возможно, выбранный столбец не сохранен должным образом или имеет проблемы с навигацией.
Ответ №3:
То, чего вы пытаетесь достичь, — это неправильное поведение, все ожидают навигации, подобной Excel, при нажатии клавиши Tab, когда сетка данных сфокусирована. Лучше запретить остановку табуляции в DataGrid, установив IsTabStop="False"
в DataGrid, если вы не хотите, чтобы пользователь перемещался по DataGrid.
Комментарии:
1. Почему за голосование «против»?! это просто мое мнение с точки зрения UX!!
2. Да, я согласен с вами, мистер Фадил, лучше установить IsTabStop=»False»
3. Извините за отрицательный ответ, но ваш ответ на самом деле совсем не помог. IsTabStop= false удаляет табуляцию в сетке, которую я хотел бы сохранить, и не приводит к желаемому поведению, когда оно сфокусировано. Конечно, во многих случаях навигация в стиле Excel работает хорошо, но в данном случае она просто избыточна, сетка доступна только для чтения и имеет выбор в виде полной строки.
4. Если это доступно только для чтения
DataGrid
, я изо всех сил пытаюсь понять, какой смысл имеет остановка табуляции…5. @Hohinhime. Вы вводите вкладку в DataGrid, затем перемещаетесь внутри DataGrid с помощью клавиш со стрелками, а когда вы хотите выйти из DataGrid, вы снова используете клавишу tab. Я думаю, что это лучший способ. И важно, чтобы при выводе табуляции из DataGrid и возвращении в нее вы возвращались в ту же ячейку, в которой находились в прошлый раз. Эта ячейка будет иметь логический фокус для DataGrid. Если у вас есть DataGrid с парой сотен ячеек, вы бы не хотели нажимать tab один раз для каждой ячейки, чтобы покинуть DataGrid.