122 votes

Lier la boîte de texte à la pression de la touche Entrée

La liaison de données par défaut sur TextBox es TwoWay et il n'engage le texte dans la propriété que lorsque TextBox a perdu son objectif.

Existe-t-il un moyen simple en XAML de faire en sorte que la liaison de données se produise lorsque j'appuie sur la touche Enter sur la touche TextBox ?. Je sais que c'est assez facile à faire dans le code derrière, mais imaginez si ceci TextBox est à l'intérieur d'un complexe DataTemplate .

151voto

Samuel Jack Points 14556

Vous pouvez vous faire une approche purement XAML en créant un fichier comportement attaché .

Quelque chose comme ça :

public static class InputBindingsManager
{

    public static readonly DependencyProperty UpdatePropertySourceWhenEnterPressedProperty = DependencyProperty.RegisterAttached(
            "UpdatePropertySourceWhenEnterPressed", typeof(DependencyProperty), typeof(InputBindingsManager), new PropertyMetadata(null, OnUpdatePropertySourceWhenEnterPressedPropertyChanged));

    static InputBindingsManager()
    {

    }

    public static void SetUpdatePropertySourceWhenEnterPressed(DependencyObject dp, DependencyProperty value)
    {
        dp.SetValue(UpdatePropertySourceWhenEnterPressedProperty, value);
    }

    public static DependencyProperty GetUpdatePropertySourceWhenEnterPressed(DependencyObject dp)
    {
        return (DependencyProperty)dp.GetValue(UpdatePropertySourceWhenEnterPressedProperty);
    }

    private static void OnUpdatePropertySourceWhenEnterPressedPropertyChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
    {
        UIElement element = dp as UIElement;

        if (element == null)
        {
            return;
        }

        if (e.OldValue != null)
        {
            element.PreviewKeyDown -= HandlePreviewKeyDown;
        }

        if (e.NewValue != null)
        {
            element.PreviewKeyDown += new KeyEventHandler(HandlePreviewKeyDown);
        }
    }

    static void HandlePreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            DoUpdateSource(e.Source);
        }
    }

    static void DoUpdateSource(object source)
    {
        DependencyProperty property =
            GetUpdatePropertySourceWhenEnterPressed(source as DependencyObject);

        if (property == null)
        {
            return;
        }

        UIElement elt = source as UIElement;

        if (elt == null)
        {
            return;
        }

        BindingExpression binding = BindingOperations.GetBindingExpression(elt, property);

        if (binding != null)
        {
            binding.UpdateSource();
        }
    }
}

Ensuite, dans votre XAML, vous définissez l'élément InputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty à celle que vous voulez mettre à jour lorsque la propriété Enter est pressée. Comme ceci

<TextBox Name="itemNameTextBox"
         Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}"
         b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text"/>

(Vous devez simplement vous assurer d'inclure une référence xmlns clr-namespace pour "b" dans l'élément Root de votre fichier XAML qui pointe vers l'espace de nom dans lequel vous avez placé l'InputBindingsManager).

0 votes

J'ai essayé <TextBox x:Name="itemNameTextBox" Text="{Binding Source, ElementName=webBrowserCustom, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text"/> mais il semble que le webbrowser ne mette pas à jour sa source. J'ai raté quelque chose ? Je n'ai pas géré d'événement. Je suppose qu'avec une liaison à deux voies, l'événement est géré automatiquement. Ai-je tort ?

11 votes

Dans le but d'épargner aux futurs lecteurs quelques minutes de bricolage, je viens d'utiliser ceci dans mon projet, et l'exemple XAML fourni ci-dessus ne fonctionnait pas correctement. La source était mise à jour à chaque changement de caractère. En remplaçant "UpdateSourceTrigger=PropertyChanged" par "UpdateSourceTrigger=Explicit", le problème a été résolu. Maintenant, tout fonctionne comme souhaité.

1 votes

@ihake : Je pense que le changement que vous recommandez empêchera également la mise à jour en cas de perte de focus.

67voto

Ben Points 856

Voici comment j'ai résolu ce problème. J'ai créé un gestionnaire d'événement spécial qui est entré dans le code derrière :

private void TextBox_KeyEnterUpdate(object sender, KeyEventArgs e)
{
    if (e.Key == Key.Enter)
    {
        TextBox tBox = (TextBox)sender;
        DependencyProperty prop = TextBox.TextProperty;

        BindingExpression binding = BindingOperations.GetBindingExpression(tBox, prop);
        if (binding != null) { binding.UpdateSource(); }
    }
}

Ensuite, j'ai ajouté ceci comme un gestionnaire d'événement KeyUp dans le XAML :

<TextBox Text="{Binding TextValue1}" KeyUp="TextBox_KeyEnterUpdate" />
<TextBox Text="{Binding TextValue2}" KeyUp="TextBox_KeyEnterUpdate" />

Le gestionnaire d'événement utilise son sender pour que sa propre liaison soit mise à jour. Comme le gestionnaire d'événements est autonome, il devrait fonctionner dans un DataTemplate complexe. Ce gestionnaire d'événement unique peut maintenant être ajouté à toutes les zones de texte qui ont besoin de cette fonctionnalité.

10 votes

Quelque chose me dit que c'est un poste sous-estimé. Beaucoup de réponses sont populaires parce qu'elles sont "XAML-y". Mais celle-ci semble permettre de gagner de la place dans la plupart des cas.

4 votes

+1 Cela vous permet également de ne pas utiliser UpdateSourceTrigger au cas où vous auriez déjà laborieusement fait en sorte que vos liaisons TextBox se comportent comme vous le souhaitez (avec des styles, une validation, un double sens, etc.), mais qu'actuellement, elles ne reçoivent pas d'entrée après avoir appuyé sur Entrée.

2 votes

C'est l'approche la plus propre que j'ai trouvée, et à mon avis, elle devrait être marquée comme la réponse. J'ai ajouté une vérification supplémentaire de nullité sur l'expéditeur, une vérification sur Key.Return (la touche Enter de mon clavier renvoie Key.Return), et Keyboard.ClearFocus() pour retirer le focus de la TextBox après avoir mis à jour la propriété source. J'ai fait des modifications à la réponse qui est en attente de révision par les pairs.

47voto

Matt Hamilton Points 98268

Je ne pense pas qu'il existe un moyen "purement XAML" de faire ce que vous décrivez. Vous pouvez configurer une liaison de manière à ce qu'elle soit mise à jour chaque fois que le texte d'une TextBox change (plutôt que lorsque la TextBox perd le focus) en définissant l'attribut Déclencheur de mise à jour de la source comme ceci :

<TextBox Name="itemNameTextBox"
    Text="{Binding Path=ItemName, UpdateSourceTrigger=PropertyChanged}" />

Si vous définissez UpdateSourceTrigger sur "Explicit" et que vous traitez l'événement PreviewKeyDown de la TextBox (en recherchant la touche Entrée), vous pourriez obtenir ce que vous voulez, mais cela nécessiterait du code-behind. Peut-être qu'une sorte de propriété attachée (similaire à ma propriété EnterKeyTraversal ) vous conviendrait.

3 votes

Cette réponse est peut-être plus simple que celle qui a été marquée comme réponse, mais elle présente certaines limites. Image vous voulez faire une sorte de vérification dans la fonction set de la propriété bound (vérifier si l'entrée est valide). Vous ne voulez pas appeler cette fonction de vérification après chaque touche sur laquelle l'utilisateur a appuyé, n'est-ce pas (surtout pas lorsque la fonction prend un certain temps) ?

25voto

Ryan Versaw Points 3682

Vous pourriez facilement créer votre propre contrôle héritant de TextBox et le réutiliser dans tout votre projet.

Quelque chose de similaire à ceci devrait fonctionner :

public class SubmitTextBox : TextBox
{
    public SubmitTextBox()
        : base()
    {
        PreviewKeyDown += new KeyEventHandler(SubmitTextBox_PreviewKeyDown);
    }

    void SubmitTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            BindingExpression be = GetBindingExpression(TextBox.TextProperty);
            if (be != null)
            {
                be.UpdateSource();
            }
        }
    }
}

Il peut y avoir un moyen de contourner cette étape, mais autrement vous devriez lier comme ceci (en utilisant Explicit) :

<custom:SubmitTextBox
    Text="{Binding Path=BoundProperty, UpdateSourceTrigger=Explicit}" />

9 votes

En WPF/Silverlight, vous ne devriez jamais utiliser l'héritage - cela perturbe les styles et n'est pas aussi flexible que les comportements attachés. Par exemple, avec les comportements attachés, vous pouvez avoir à la fois un filigrane et UpdateOnEnter sur la même zone de texte.

6voto

franssu Points 1333

Je pense qu'il y a une erreur dans la réponse de Samuel Jack :

b:InputBindingsManager.UpdatePropertySourceWhenEnterPressedProperty="TextBox.Text"

devrait être :

b:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text"

Cela a fait que mon VS2010 n'était pas du tout content lorsque j'ai copié/collé et essayé d'exécuter mon application.

PS : Je ne peux pas commenter la réponse de Samuel car je n'ai pas assez de réputation ; comment font les nouveaux utilisateurs lorsqu'ils voient une erreur dans les réponses des autres ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X