Ви движемся через DataGrid

Я использую DataGrid для отображения группы данных. я имею SelectionMode="Extended" а также SelectionUnit="FullRow",

То, что я хотел бы сделать, это нажать J чтобы переместить фокус вниз в сетке, нажмите K чтобы двигаться вверх в сетке, и нажмите x добавить / удалить выделенную строку в / из списка SelectedItems(в основном, как в Gmail с горячими клавишами)

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

Вот что я пробовал до сих пор

case Key.X:
{
    resultsGrid.SelectedItems.Add(resultsGrid.SelectedItem);
    e.Handled = true;
    break;
}
case Key.J:
{
    //down
    var currow = (DataGridRow) resultsGrid.ItemContainerGenerator.ContainerFromItem(resultsGrid.SelectedItem);
    currow.MoveFocus(new TraversalRequest(FocusNavigationDirection.Down));
    //if (resultsGrid.SelectedIndex + 1 >= resultsGrid.Items.Count)
    //    resultsGrid.SelectedIndex = 0;
    //else
    //    resultsGrid.SelectedIndex++;
    break;
}
case Key.K:
{
    //up
    var currow =
        (DataGridRow) resultsGrid.ItemContainerGenerator.ContainerFromItem(resultsGrid.SelectedItem);
    currow.MoveFocus(new TraversalRequest(FocusNavigationDirection.Up));
    //if (resultsGrid.SelectedIndex - 1 <= 0)
    //    resultsGrid.SelectedIndex = resultsGrid.Items.Count - 1;
    //else
    //    resultsGrid.SelectedIndex--;
    break;
}

В настоящее время текущая строка не перемещается вверх или вниз. Я также пытался FocusNavigationDirection.Previous а также Next и те не перемещают центр также. Если я иду по индексу, он перемещается, но нажатие X не добавляет в список выбранных элементов. Кажется, что мультиселектор не хочет включаться, пока вы не используете Shift и Up / Down или Shift мыши

редактировать

Итак, я понял, как перемещаться с помощью j а также k ключ, но выбор по-прежнему не работает. Если я перемещаюсь вверх или вниз, это очищает выделение, также нажав x ничего не делает, по крайней мере визуально.

case Key.X:
    resultsGrid.SelectedItems.Add(resultsGrid.SelectedItem);
    e.Handled = true;
    break;
case Key.J:
    {
        //down
        InputManager.Current.ProcessInput(new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, Key.Down)
        {
            RoutedEvent = Keyboard.KeyDownEvent
        });
        resultsGrid.ScrollIntoView(resultsGrid.SelectedItem);
        e.Handled = true;
        break;
    }
case Key.K:
    {
        //up
        InputManager.Current.ProcessInput(new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, Key.Up)
        {
            RoutedEvent = Keyboard.KeyDownEvent
        });
        resultsGrid.ScrollIntoView(resultsGrid.SelectedItem);
        e.Handled = true;
        break;
    }

1 ответ

Решение

Если я правильно понимаю проблему - у вас есть фокус и выбор в каждой строке. Вы хотите переместить фокус с помощью клавиш k/j и переключить выбор с помощью клавиши x.

Мне нравится использовать поведение в этих ситуациях - для этого требуется ссылка на System.Windows.Interactivity.dll из blen SDK, но также для более чистого и модульного кода.

[edit: это быстрый POC, который я сделал. Это, вероятно, потребует некоторой дополнительной защиты от нуля и обработки краевых случаев.]

Поведение это:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace GridNavigationTest
{
    public class GridNavigationBehavior : Behavior<DataGrid>
    {
        #region Overrides of Behavior
        /// <summary>
        /// Called after the behavior is attached to an AssociatedObject.
        /// </summary>
        /// <remarks>
        /// Override this to hook up functionality to the AssociatedObject.
        /// </remarks>
        protected override void OnAttached()
        {
            AssociatedObject.PreviewKeyDown += AssociatedObject_KeyDown;
        }

        /// <summary>
        /// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
        /// </summary>
        /// <remarks>
        /// Override this to unhook functionality from the AssociatedObject.
        /// </remarks>
        protected override void OnDetaching()
        {
            AssociatedObject.KeyDown -= AssociatedObject_KeyDown;
        }
        #endregion

        #region Event handlers
        void AssociatedObject_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.J:
                    NavigateGridFocus(FocusNavigationDirection.Up);
                    break;
                case Key.K:
                    NavigateGridFocus(FocusNavigationDirection.Down);
                    break;
                case Key.X:
                    ToggleRowSelection();
                    break;
            }
        } 
        #endregion

        #region Methods
        private void ToggleRowSelection()
        {
            var currentlyFocusedRow = FindCurrentlyFocusedRow();
            if (currentlyFocusedRow == null)
            {
                return;
            }

            var generator = AssociatedObject.ItemContainerGenerator;
            var rowItem = generator.ItemFromContainer(currentlyFocusedRow);
            if (AssociatedObject.SelectionMode == DataGridSelectionMode.Extended)
            {
                if (AssociatedObject.SelectedItems.Contains(rowItem))
                {
                    AssociatedObject.SelectedItems.Remove(rowItem);
                }
                else
                {
                    AssociatedObject.SelectedItems.Add(rowItem);
                }
            }
            else
            {
                AssociatedObject.SelectedItem = AssociatedObject.SelectedItem == rowItem ? null : rowItem;
            }
        }

        private void NavigateGridFocus(FocusNavigationDirection direction)
        {
            var currentlyFocusedRow = FindCurrentlyFocusedRow();
            if (currentlyFocusedRow == null)
            {
                return;
            }

            var traversalRequest = new TraversalRequest(direction);
            var currentlyFocusedElement = Keyboard.FocusedElement as UIElement;
            if (currentlyFocusedElement != null) currentlyFocusedElement.MoveFocus(traversalRequest);
        }

        private DataGridRow FindCurrentlyFocusedRow()
        {
            var generator = AssociatedObject.ItemContainerGenerator;
            if (generator.Status != GeneratorStatus.ContainersGenerated)
            {
                return null;
            }

            for (var index = 0; index < generator.Items.Count - 1; index++)
            {
                var row = generator.ContainerFromIndex(index) as DataGridRow;
                if (row != null && row.IsKeyboardFocusWithin)
                {
                    return row;
                }
            }
            return null;
        }
        #endregion
    }
} 

И использование это:

<Window x:Class="GridNavigationTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:gridNavigationTest="clr-namespace:GridNavigationTest"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        Title="MainWindow"
        Height="350"
        Width="525">
    <Grid>
        <DataGrid ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type gridNavigationTest:MainWindow}}, Path=People}">
            <i:Interaction.Behaviors>
                <gridNavigationTest:GridNavigationBehavior/>
            </i:Interaction.Behaviors>
            <!--This is here just for testing of focus movement-->
            <DataGrid.ItemContainerStyle>
                <Style TargetType="{x:Type DataGridRow}">
                    <Style.Triggers>
                        <Trigger Property="IsKeyboardFocusWithin"
                                 Value="True">
                            <Setter Property="Background"
                                    Value="HotPink" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </DataGrid.ItemContainerStyle>
        </DataGrid>
    </Grid>
</Window>

Это требует, чтобы одна из строк имела IsKeyboardFocuedWithin установите в true. Вы можете испечь логику для первоначального выбора в поведении (или в новом поведении).

Другие вопросы по тегам