Tag Archives: Windows Forms

[WPF] Déclarer des raccourcis clavier globaux en XAML avec NHotkey

Un besoin fréquent pour les applications de bureau est de gérer des raccourcis claviers globaux, pour pouvoir réagir aux raccourcis même quand l’application n’a pas le focus. Malheureusement, il n’y aucune fonctionnalité intégrée dans le .NET Framework pour gérer ça.

Bien sûr, le problème n’est pas nouveau, et il y a un certain nombre de librairies open-source qui se proposent d’y remédier (par exemple VirtualInput). La plupart d’entre elles sont basées sur des hooks système globaux, ce qui leur permet d’intercepter toutes les frappes de touche, même celles qui ne vous intéressent pas. J’ai déjà utilisé certaines de ces librairies, mais je n’en suis pas vraiment satisfait :

  • elles sont souvent liées à un framework IHM spécifique (généralement Windows Forms), ce qui les rend peu pratiques à utiliser avec un autre framework (comme WPF)
  • je n’aime pas trop l’approche consistant à intercepter toutes les frappes de touche. Cela a généralement pour conséquence d’écrire un grosse méthode avec un paquet de if/else if pour décider quoi faire en fonction de la combinaison de touches qui a été pressée

Une meilleure option, à mon sens, est d’écouter uniquement les touches qui vous intéressent, et de spécifier de façon déclarative l’action à effectuer pour chacune d’entre elles. L’approche utilisée en WPF pour les KeyBindings est assez élégante :

<Window.InputBindings>
    <KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}" />
    <KeyBinding Gesture="Ctrl+Alt+Subtract" Command="{Binding DecrementCommand}" />
</Window.InputBindings>

Mais bien sûr, les KeyBindings ne fonctionnent pas de façon globale, ils nécessitent que votre application ait le focus… Et si on pouvait changer ça ?

NHotkey est une librairie très simple pour gérer les raccourcis clavier, qui permet entre autres d’avoir des KeyBindings globaux. Il suffit de mettre à true une propriété attachée sur le KeyBinding :

<Window.InputBindings>
    <KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}"
                HotkeyManager.RegisterGlobalHotkey="True" />
    <KeyBinding Gesture="Ctrl+Alt+Subtract" Command="{Binding DecrementCommand}"
                HotkeyManager.RegisterGlobalHotkey="True" />
</Window.InputBindings>

Et c’est tout ; les commandes définies sur les KeyBindings seront maintenant invoquées même si votre application n’a pas le focus !

Vous pouvez aussi utiliser NHotkey depuis le code :

HotkeyManager.Current.AddOrReplace("Increment", Key.Add, ModifierKeys.Control | ModifierKeys.Alt, OnIncrement);
HotkeyManager.Current.AddOrReplace("Decrement", Key.Subtract, ModifierKeys.Control | ModifierKeys.Alt, OnDecrement);

La librairie tire partie de la fonction RegisterHotkey. Parce qu’elle supporte également Windows Forms, elle est constituée de 3 parties distinctes, afin de ne pas avoir à référencer l’assembly Windows Forms depuis une appli WPF, ou vice versa :

  • La librarie “core”, qui gère l’enregistrement des hotkeys proprement dit, indépendamment d’un framework IHM spécifique. Cette librairie n’est pas directement utilisable, mais elle est utilisée par les deux autres.
  • L’API spécifique à WinForms, qui utilise l’énumération Keys de System.Windows.Forms.
  • L’API spécifique à WPF, qui utilise les énumérations Key et ModifierKeys de System.Windows.Input, et supporte les KeyBindings globaux en XAML.

Si vous installez la librairie à l’aide de Nuget, ajoutez l’un ou l’autre des packages NHotkey.Wpf ou NHotkey.WindowsForms ; le package “core” sera automatiquement ajouté.

[Windows Forms] Glisser-déplacer automatique de contrôles (DragMove)

Je ressors de mes tiroirs un code que j’avais écrit il y a quelque temps, et dont je voudrais vous faire profiter…

Il existe en WPF une méthode très pratique pour déplacer une fenêtre sans bordure : Window.DragMove. Elle s’utilise comme ceci :

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            this.DragMove();
        }

A partir de l’appel de cette méthode, la fenêtre est déplacée avec la souris jusqu’à ce que le bouton soit relâché. Difficile de faire plus simple 😉

Malheureusement, cette méthode n’existe qu’en WPF, et une majorité de développeurs travaillent encore avec Windows Forms. Je vous propose donc un code qui permet d’utiliser en Windows Forms la technique décrite ci-dessus, en y apportant les améliorations suivantes :

  • Utilisable sur n’importe quel contrôle, pas seulement sur une fenêtre
  • Pas d’obligation de gérer l’évènement MouseDown
  • Intégration au designer par un IExtenderProvider

Ma solution se compose des 2 éléments suivants :

  • une classe statique DragMoveExtensions qui fournit des méthodes d’extensions pour la classe Control (facilement transformables en simples méthodes statiques pour l’utilisation en C# 2)
  • un composant DragMoveProvider, qui implémente IExtenderProvider pour ajouter une propriété EnableDragMove aux contrôles de la Form

Pour l’utiliser, il y en a pour tous les goûts 😉

  • La méthode la plus simple, sans écrire une seule ligne de code : en mode design, poser un DragMoveProvider sur la Form, et positionner à true la propriété EnableDragMove sur la Form ou le contrôle
  • DragMoveProvider
    DragMoveProvider
  • La méthode la plus proche de WPF : dans le handler de l’évènement MouseDown, appeler la méthode d’extension DragMove sur la Form ou le contrôle à déplacer
  •         private void label2_MouseDown(object sender, MouseEventArgs e)
            {
                label2.DragMove();
            }
    
  • La méthode la plus souple : appeler, au cas par cas, la méthode d’extension EnableDragMove sur la Form ou le contrôle à déplacer (aucun évènement à gérer).
  •         private void checkBox1_CheckedChanged(object sender, EventArgs e)
            {
                this.EnableDragMove(checkBox1.Checked);
            }
    

La solution en pièce jointe contient la librairie réutilisable WinFormsDragMove, ainsi qu’un projet de test qui illustre les différentes manières d’utiliser cette librairie. Une version compatible C# 2 des ces projets est également inclue.

Télécharger les sources