0

我正在使用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.PreviousNext那些也没有移动焦点。如果我按索引移动,它会移动,但按 X 不会添加到选定项目列表中。在您使用 shift 和 up/down 或 shift 鼠标单击之前,似乎不想启动多选

编辑

好的,所以我已经弄清楚如何使用jandk键进行导航,但是选择仍然不起作用。如果我向上或向下移动它会清除选择,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;
    }
4

1 回答 1

1

如果我正确理解了这个问题 - 你每行都有一个焦点和选择。您想通过 k/j 键移动焦点并通过 x 键切换选择。

我喜欢在这些情况下使用行为——它需要从 blen SDK 引用 System.Windows.Interactivity.dll,但它也使得代码更清晰和模块化。

[编辑:这是我做的一个快速 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设置为真。您可以将初始选择的逻辑烘焙到行为中(或者,在新行为中)。

于 2013-09-09T21:30:56.480 回答