web-dev-qa-db-fra.com

Passage au prochain contrôle sur la touche Entrée du clavier dans WPF

Je souhaite passer au contrôle suivant lorsque j'appuie sur la touche Entrée au lieu de la touche Tab dans une application WPF MVVM. Comment puis-je atteindre cet objectif?

30
Kishore Kumar

Si vous voulez que cela ne fonctionne que pour quelques zones de texte, La réponse de Jay est préférable. 

Si vous voulez que toute votre application fonctionne de cette façon, la réponse de makwana.a est meilleure, mais peut être améliorée. 

Ci-dessous ma modification de la réponse de makwana.a , que j’ai utilisée dans de nombreuses applications. Il inclut également une assistance pour passer au contrôle suivant via enter si le contrôle actif est une case à cocher. Au lieu d'utiliser la propriété tag pour décider si le focus doit ou non être déplacé, j'ai utilisé la propriété AcceptsReturn de la zone de texte. Je l'ai fait parce que la valeur par défaut est false et que la valeur true est définie sur les zones de texte multilignes. Dans ce cas, vous ne voudrez pas que le focus se déplace de toute façon sur le contrôle suivant.

Déclarez ces gestionnaires d'événements dans l'anneau OnStartup d'App.xaml

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

Voici le reste des méthodes nécessaires pour que l'application fonctionne dans son ensemble.

    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;
        }
    }

Modifier

J'ai mis à jour le code pour marquer la frappe comme étant gérée si le mouvement a réussi et aussi basculer la case à cocher puisque la clé a été gérée et ne pourra plus l'atteindre.

26
StillLearnin

Ci-dessous, une propriété attachée que j'ai utilisée pour cela.

Tout d'abord, exemple d'utilisation:

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

(UI est l'alias de l'espace de noms pour lequel j'ai défini ce qui suit.)

La propriété attachée:

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));
    }
}

Vous avez également dit "au lieu de tabulation", donc je me demande si vous souhaitez supprimer la possibilité d'utiliser tabulation de la manière habituelle. Je le déconseille, car il s'agit d'un paradigme commun bien connu, mais si c'est le cas, vous pouvez ajouter un gestionnaire PreviewKeyDown dans la propriété attachée, rechercher la clé de tabulation et définir Handled = true pour les arguments d'événement.

41
Jay

exemple de solution: utilisation de PreviewKeyDown dans le panneau de pile. L'aperçu ... est une bulle qui permet de gérer l'événement à un niveau supérieur. Vous devrez peut-être gérer cela différemment pour différents types d'élément, comme le bouton, il semble que vous devez conserver la touche Entrée et ne pas modifier le focus sur la touche Entrée. 

Voici le xaml:

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

Et voici le code derrière:

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;
    }
}

Ceci est seulement un bac à sable pour la preuve de concept.

Bonne codage ...

11
Paul Matovich

J'espère que cette aide: utilisez AttachedProperty http://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">
3
MasterLuV

Écrire ce code dans l'événement onstartup de votre fichier d'application

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

puis définissez TextBox_KeyDown sub comme

 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

J'ai utilisé la propriété "tag" de la zone de texte pour éviter le déplacement du focus. c'est-à-dire si vous ne voulez pas passer au contrôle suivant une fois sur la touche Entrée (dans le cas d'une zone de texte multiligne où la saisie est requise pour créer une nouvelle ligne). Il suffit de définir la propriété tag à 1. 

3
makwana.a

Il faut d’abord ajouter un déclencheur à chaque élément à invoquer lors du déclenchement de PreviewKeyDown. Ajoutez également la propriété Dependency et liez FrameworkElement à qui vous ne voulez pas mettre l'accent. Dans le déclencheur, définissez Focus sur l'élément lié.

1
Anatolii Gabuza

Utiliser code-behind:

Je suis venu avec le code ci-dessous. Notez que cela ne définit pas e.Handled. De plus, MoveFocus_Next ne renvoie pas si le focus du déplacement a réussi, mais si l'argument n'est pas null. Vous pouvez ajouter ou supprimer des types de contrôles à manipuler selon vos besoins. Le code a été écrit pour la fenêtre principale de l'application, mais gère également les autres fenêtres. Vous pouvez également adapter le code pour l'appel à partir de l'événement 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;
            }
        }
    }
}
0
Thejaka Maldeniya