[UWP] Exécuter une commande sur pression de la touche Entrée

Avec UWP/XAML, il n’est pas possible de lier des control UWP/XAML à un formulaire comme on pourrait le faire pour du web. Dans un TextBox, on ne peut donc pas déclencher une commande sur la pression de la touche Entrée. Dommage…

Heureusement, nos controls ont des events… oui, mais est-ce que cela respecte notre pattern MVVM ? (ou anti-pattern comme disent certain)

Avec MVVM, il y a quelques scénarios simples qui deviennent vite un enfer. Bien souvent, ce n’est pas la faute de MVVM, mais des développeurs qui deviennent de vrais extrémistes du zéro code C# derrière la vue. Certes, mois il y a de code, et moins on risque d’avoir affaire à un code spaghetti.

Pour répondre à la contrainte zéro code behind, J’utilise très souvent des attached roperties. Cela permet d’ajouter simplement des comportements à un control sans partir dans du control custom.

Imaginons un VieModel qui dispose d’une commande SearchCommand et d’un text Query. On veut que la commande soit utilisée quand l’utilisateur presse la touche Entrée du clavier.

Pour ajouter ce comportement via un Binding, j’ai codé une Attached Properties EnterCommand que j’attache à une TextBox et sur laquelle je Bind ma commande. Le code Xaml résultant est donc propre et simple:


<TextBox
Text="{Binding Query, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
my:TextBox.EnterCommand="{Binding SearchCommand, Mode=OneTime}" />

Je n’utilise pas de Binding compilé pour ce type de scénario, car celui-ci ne dispose pas d'un UpdateSourceTrigger. Celui-ci est indispensable pour que notre propriété soit mise à jour avant que la touche Entrée ne soit utilisée.

En ce qui concerne l’attached propertie, la seule subtilité est dans le unload du control que j’utilise pour supprimer mes handlers sur le control (cela permet de s’assurer que l’on ne concerne pas de référence sur le control ou le ViewModel, et donc que l’on libère les ressource quand elles ne sont plus utiles).



using System.Windows.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;

namespace MyLib.Xaml
{
    public static class TextBox
    {
        #region Déclarations

        private const string EnterCommandPropertyName = "EnterCommand";
        public static readonly DependencyProperty EnterCommandProperty;

        #endregion

        #region Constructeur

        /// <summary>
        /// Constructeur static
        /// </summary>
        static TextBox()
        {
            EnterCommandProperty = DependencyProperty.RegisterAttached(
                EnterCommandPropertyName, typeof(ICommand), typeof(TextBox), new PropertyMetadata(null, EnterCommandChanged));
        }

        #endregion

        #region Proriétés

        public static ICommand GetEnterCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(EnterCommandProperty);
        }

        public static void SetEnterCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(EnterCommandProperty, value);
        }

        #endregion

        #region Méthodes

        /// <summary>
        /// EnterCommand à changé
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void EnterCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Récupération du control
            if (!(d is Windows.UI.Xaml.Controls.TextBox control)) return;
            control.KeyUp += OnControlKeyUp;
            control.Unloaded += OnControlUnloaded;
        }

        // Récupération de la commandes
        private static void OnControlKeyUp(object sender, KeyRoutedEventArgs eventarg)
        {
            // Test si l'utilisateur a utilisé la touche Enter
            if (eventarg.Key != Windows.System.VirtualKey.Enter) return;
            // Récupératin et test du control
            if (!(sender is Windows.UI.Xaml.Controls.TextBox control)) return;
            // Récupération de la commande
            ICommand command = GetEnterCommand(control);
            // Execution
            command?.Execute(null);
        }

        private static void OnControlUnloaded(object sender, RoutedEventArgs e)
        {
            // Récupération du control
            if (!(sender is Windows.UI.Xaml.Controls.TextBox control)) return;
            control.KeyUp -= OnControlKeyUp;
            control.Unloaded -= OnControlUnloaded;
        }

        #endregion
    }
}

Jérémy Jeanson

Comments

You have to be logged in to comment this post.