Переместить фокус на следующую ячейку при нажатии клавиши Enter в WPF DataGrid?

Я хочу иметь Custom DataGrid, который может,

  1. Переход к следующей ячейке при нажатии клавиши Enter также, если она находится в режиме редактирования.
  2. Когда будет достигнут последний столбец в текущей строке, фокус должен переместиться на первую ячейку следующей строки.
  3. При достижении следующей ячейки, если ячейка является редактируемой, она должна автоматически стать редактируемой.
  4. Если ячейка содержит 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;
                }
            }
        }
    }
Другие вопросы по тегам