移动到WPF中Enter键上的下一个控件



当我在WPF MVVM应用程序中按Enter键而不是Tab键时,我想移动到下一个控件。我怎样才能做到这一点?

下面是我为此使用的附加属性。

首先,示例用法:

<TextBox Width="100"
         Text="{Binding Name, Mode=TwoWay}"
         UI:FocusAdvancement.AdvancesByEnterKey="True" />

(UI是我定义以下内容的名称空间别名。(

所附财产:

public static class FocusAdvancement
{
    public static bool GetAdvancesByEnterKey(DependencyObject obj)
    {
        return (bool)obj.GetValue(AdvancesByEnterKeyProperty);
    }
    public static void SetAdvancesByEnterKey(DependencyObject obj, bool value)
    {
        obj.SetValue(AdvancesByEnterKeyProperty, value);
    }
    public static readonly DependencyProperty AdvancesByEnterKeyProperty =
        DependencyProperty.RegisterAttached("AdvancesByEnterKey", typeof(bool), typeof(FocusAdvancement), 
        new UIPropertyMetadata(OnAdvancesByEnterKeyPropertyChanged));
    static void OnAdvancesByEnterKeyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var element = d as UIElement;
        if(element == null) return;
        if ((bool)e.NewValue) element.KeyDown += Keydown;
        else element.KeyDown -= Keydown;
    }
    static void Keydown(object sender, KeyEventArgs e)
    {
        if(!e.Key.Equals(Key.Enter)) return;
        var element = sender as UIElement;
        if(element != null) element.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
    }
}

你还说"而不是tab",所以我想知道你是否想抑制以通常方式使用tab的能力。我建议不要这样做,因为这是一种常见的、众所周知的范式,但如果是这样的话,您可以在附加的属性中添加一个PreviewKeyDown处理程序,检查tab键,并为事件参数设置Handled = true

如果你只想让它适用于几个文本框,周的答案是最好的。

如果你想让整个应用程序都以这种方式运行,makwana.a的答案更好,但可以改进。

以下是我对makwana.a答案的修改,我已经在许多应用中使用过它。它还支持在活动控件为复选框的情况下通过回车移动到下一个控件。我没有使用标记属性来决定焦点是否应该移动,而是使用了文本框的AcceptsReturn属性。我这样做是因为它默认为false,并且只在多行文本框中设置为true。在这种情况下,您无论如何都不希望焦点移动到回车时的下一个控件。

在App.xaml 的OnStartup void中声明这些事件处理程序

        EventManager.RegisterClassHandler(typeof(TextBox), TextBox.KeyDownEvent, new KeyEventHandler(TextBox_KeyDown));
        EventManager.RegisterClassHandler(typeof(CheckBox), CheckBox.KeyDownEvent, new KeyEventHandler(CheckBox_KeyDown));

以下是使其在应用程序范围内工作所需的其余方法。

    void TextBox_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter & (sender as TextBox).AcceptsReturn == false) MoveToNextUIElement(e);
    }
    void CheckBox_KeyDown(object sender, KeyEventArgs e)
    {
        MoveToNextUIElement(e);
        //Sucessfully moved on and marked key as handled.
        //Toggle check box since the key was handled and
        //the checkbox will never receive it.
        if (e.Handled == true)
        {
            CheckBox cb = (CheckBox)sender;
            cb.IsChecked = !cb.IsChecked;
        }
     }
    void MoveToNextUIElement(KeyEventArgs e)
    {
        // Creating a FocusNavigationDirection object and setting it to a
        // local field that contains the direction selected.
        FocusNavigationDirection focusDirection = FocusNavigationDirection.Next;
        // MoveFocus takes a TraveralReqest as its argument.
        TraversalRequest request = new TraversalRequest(focusDirection);
        // Gets the element with keyboard focus.
        UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
        // Change keyboard focus.
        if (elementWithFocus != null)
        {
            if (elementWithFocus.MoveFocus(request)) e.Handled = true;
        }
    }

编辑

我更新了代码,如果移动成功,则将击键标记为已处理,并且由于键已处理且将不再到达,因此我还切换复选框。

示例解决方案:在堆栈面板中使用PreviewKeyDown。预览。。。是一个气泡,因此事件可以在更高级别上处理。对于不同的元素类型,您可能需要以不同的方式处理这一问题,比如按钮,它似乎应该保留回车键,而不是将焦点更改为回车键。

这是xaml:

<StackPanel PreviewKeyDown="StackPanel_PreviewKeyDown" >
    <TextBox >
        Hello
    </TextBox>
    <TextBox>
        World
    </TextBox>
    <TextBox>
        test
    </TextBox>
</StackPanel>

下面是代码:

private void StackPanel_PreviewKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter)
    {
        TextBox s = e.Source as TextBox;
        if (s != null)
        {
            s.MoveFocus(new TraversalRequest( FocusNavigationDirection.Next));
        }
        e.Handled = true;
    }
}

这只是一个概念验证的沙盒。

快乐编码。。。

希望得到帮助:使用AttachedPropertyhttp://madprops.org/blog/enter-to-tab-as-an-attached-property/

public class EnterKeyTraversal
{
    public static bool GetIsEnabled(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsEnabledProperty);
    }
    public static void SetIsEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(IsEnabledProperty, value);
    }
    static void ue_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    {
        var ue = e.OriginalSource as FrameworkElement;
        if (e.Key == Key.Enter)
        {
            e.Handled = true;
            ue.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        }
    }
    private static void ue_Unloaded(object sender, RoutedEventArgs e)
    {
        var ue = sender as FrameworkElement;
        if (ue == null) return;
        ue.Unloaded -= ue_Unloaded;
        ue.PreviewKeyDown -= ue_PreviewKeyDown;
    }
    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.RegisterAttached("IsEnabled", typeof(bool),
        typeof(EnterKeyTraversal), new UIPropertyMetadata(false, IsEnabledChanged));
    static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var ue = d as FrameworkElement;
        if (ue == null) return;
        if ((bool)e.NewValue)
        {
            ue.Unloaded += ue_Unloaded;
            ue.PreviewKeyDown += ue_PreviewKeyDown;
        }
        else
        {
            ue.PreviewKeyDown -= ue_PreviewKeyDown;
        }
    }
}

<StackPanel my:EnterKeyTraversal.IsEnabled="True">

在应用程序文件的onstartup事件中编写此代码

EventManager.RegisterClassHandler(GetType(TextBox), TextBox.KeyDownEvent, New RoutedEventHandler(AddressOf TextBox_KeyDown))

然后将TextBox_KeyDown子定义为

 Private Sub TextBox_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Input.KeyEventArgs)
    If e.Key = Key.Enter And TryCast(sender, TextBox).Tag <> "1" Then
        ' Creating a FocusNavigationDirection object and setting it to a
        ' local field that contains the direction selected.
        Dim focusDirection As FocusNavigationDirection = FocusNavigationDirection.Next
        ' MoveFocus takes a TraveralReqest as its argument.
        Dim request As New TraversalRequest(focusDirection)
        ' Gets the element with keyboard focus.
        Dim elementWithFocus As UIElement = TryCast(Keyboard.FocusedElement, UIElement)
        ' Change keyboard focus.
        If elementWithFocus IsNot Nothing Then
            elementWithFocus.MoveFocus(request)
        End If
    End If
End Sub

我使用了文本框的"标记"属性来跳过移动焦点。也就是说,如果您不想在按下回车键时移动到下一个控件(在多行文本框的情况下,需要回车来创建新行(。只需将标记属性设置为1。

第一个将触发器添加到PreviewKeyDown激发时将调用的每个元素。还要添加Dependency属性并绑定您习惯于将焦点放在的FrameworkElement。在触发器中,为绑定的元素提供设置Focus

使用代码隐藏:

我想出了下面的代码。请注意,它没有设置e.Handled。此外,MoveFocus_Next不会返回移动焦点是否成功,而是返回参数是否为null。您可以根据需要添加或删除要处理的控件类型。该代码是为应用程序的MainWindow编写的,但也处理其他窗口。您还可以调整代码以从App_Startup事件进行调用。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
public partial class MainWindow : Window
{
    private bool MoveFocus_Next(UIElement uiElement)
    {
        if (uiElement != null)
        {
            uiElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
            return true;
        }
        return false;
    }
    public MainWindow()
    {
        InitializeComponent();
    }
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        EventManager.RegisterClassHandler(typeof(Window), Window.PreviewKeyUpEvent, new KeyEventHandler(Window_PreviewKeyUp));
    }
    private void Window_PreviewKeyUp(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            IInputElement inputElement = Keyboard.FocusedElement;
            if (inputElement != null)
            {
                System.Windows.Controls.Primitives.TextBoxBase textBoxBase = inputElement as System.Windows.Controls.Primitives.TextBoxBase;
                if (textBoxBase != null)
                {
                    if (!textBoxBase.AcceptsReturn)
                        MoveFocus_Next(textBoxBase);
                    return;
                }
                if (
                    MoveFocus_Next(inputElement as ComboBox)
                    ||
                    MoveFocus_Next(inputElement as Button)
                    ||
                    MoveFocus_Next(inputElement as DatePicker)
                    ||
                    MoveFocus_Next(inputElement as CheckBox)
                    ||
                    MoveFocus_Next(inputElement as DataGrid)
                    ||
                    MoveFocus_Next(inputElement as TabItem)
                    ||
                    MoveFocus_Next(inputElement as RadioButton)
                    ||
                    MoveFocus_Next(inputElement as ListBox)
                    ||
                    MoveFocus_Next(inputElement as ListView)
                    ||
                    MoveFocus_Next(inputElement as PasswordBox)
                    ||
                    MoveFocus_Next(inputElement as Window)
                    ||
                    MoveFocus_Next(inputElement as Page)
                    ||
                    MoveFocus_Next(inputElement as Frame)
                )
                    return;
            }
        }
    }
}

最新更新