WPat datagrid и ключ табуляции

Другой вопрос о привязке к datagrid

У меня есть datagrid. Он имеет режим выбора, установленный для FullRow и KeyboardNavigation.TabNavigation = "Once", который, как я надеялся, получит желаемый результат, но это не так.

Когда клавиша табуляции нажимается, когда объект данных имеет фокус, он поочередно накладывается на каждый столбец сетки. Поэтому, если я вхожу в сетку с 4 столбцами, мне нужно будет нажать вкладку 4 раза, чтобы перейти к следующему tabindex.

Я хочу, чтобы клавиша табуляции сразу же вышла из таблицы данных при первом нажатии и дала фокус следующему tabindex... если это имеет смысл.

Я попытался переопределить ключ табуляции в обработчике событий 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, но тогда это должно сделать ключ вкладки ничего не делать, кроме как написать на консоль или вызвать исключение. Заставляет меня думать, что невозможно переопределить поведение вкладки.

Надеемся на полезный ввод. Как всегда, спасибо заранее.

Ответ 1

Я хотел это для своего программного обеспечения для бизнеса, и единственным способом, который я нашел для его решения, является использование кода, используя события PreviewKeyDown, GotKeyboardFocus и LostKeyboardFocus в файле данных. Я поместил эти обработчики событий в декоратор WPF, чтобы избежать повторения его для каждого отдельного DataGrid. Вероятно, возможно было бы подклассифицировать DataGrid, но я этого не пробовал.

Код для обработчиков следующий (DataGrid - это x: Name= "grid" для этого примера кода):

        private IInputElement lastDataGridFocus = null;
    private int selectedcolumnindex = 0;

    void grid_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
    {
        if (grid.Items.Count > 0 && (e.NewFocus is DataGrid || (e.NewFocus is DataGridCell && !(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 && e.Key == Key.Tab)
        {
            lastDataGridFocus = Keyboard.FocusedElement;
            grid.MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
            e.Handled = true;
        }
        else if (Keyboard.Modifiers == ModifierKeys.None && 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;
        }
    }

С помощью этого кода вы можете перемещаться внутри сетки с помощью клавиш курсора, а клавиша табуляции и клавиша shift-tab выталкивают вас из набора данных. Если вы выходите из сетки и возвращаетесь в сетку, вы также попадаете в ту же ячейку, что и вы. Это то, что хотят мои пользователи и я, и это IMHO, что элемент управления DataGrid должен предоставлять как поведение по умолчанию.

Ответ 2

То, что вы пытаетесь достичь, - это не правильное поведение, все ожидают, что Excel похож на навигацию при нажатии Tab-Key, в то время как DataGrid фокусируется. Лучше предотвратить блокировку вкладок в DataGrid, установив IsTabStop="False" в DataGrid, если вы не хотите, чтобы пользователь перемещался через DataGrid.