Comment puis-je enregistrer une touche de raccourci globale pour dire CTRL+SHIFT+(LETTER) en utilisant WPF et .NET 3.5 ?

StackOverflow https://stackoverflow.com/questions/48935

Question

Je construis une application en C# en utilisant WPF.Comment puis-je me lier à certaines clés ?

Aussi, comment puis-je me lier au Clé Windows?

Était-ce utile?

La solution

Je ne suis pas sûr de ce que vous entendez par "global" ici, mais voilà (je suppose que vous voulez dire une commande au niveau de l'application, par exemple, Sauver tous qui peut être déclenché de n'importe où par Ctrl + Changement + S.)

Vous trouvez le global UIElement de votre choix, par exemple, la fenêtre de niveau supérieur qui est le parent de tous les contrôles pour lesquels vous avez besoin de cette liaison.En raison du « bullage » des événements WPF, les événements au niveau des éléments enfants remonteront jusqu'à la racine de l'arborescence de contrôle.

Maintenant, vous avez d'abord besoin

  1. pour lier le Key-Combo avec une commande en utilisant un InputBinding comme ça
  2. vous pouvez ensuite connecter la commande à votre gestionnaire (par ex.code qui est appelé par SaveAll) par l'intermédiaire d'un CommandBinding.

Pour le les fenêtres Clé, tu utilises le droit Clé membre énuméré, Key.LWin ou Key.RWin

    public WindowMain()
    {
       InitializeComponent();
       // Bind Key
       InputBinding ib = new InputBinding(
           MyAppCommands.SaveAll,
           new KeyGesture(Key.S, ModifierKeys.Shift | ModifierKeys.Control));
       this.InputBindings.Add(ib);
       // Bind handler
       CommandBinding cb = new CommandBinding( MyAppCommands.SaveAll);
       cb.Executed += new ExecutedRoutedEventHandler( HandlerThatSavesEverthing );
       this.CommandBindings.Add (cb );
    }

    private void HandlerThatSavesEverthing (object obSender, ExecutedRoutedEventArgs e)
    {
      // Do the Save All thing here.
    }

Autres conseils

Il s'agit d'une solution entièrement fonctionnelle, j'espère que cela vous aidera...

Usage:

_hotKey = new HotKey(Key.F9, KeyModifier.Shift | KeyModifier.Win, OnHotKeyHandler);

...

private void OnHotKeyHandler(HotKey hotKey)
{
    SystemHelper.SetScreenSaverRunning();
}

Classe:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Mime;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;

namespace UnManaged
{
    public class HotKey : IDisposable
    {
        private static Dictionary<int, HotKey> _dictHotKeyToCalBackProc;

        [DllImport("user32.dll")]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, UInt32 fsModifiers, UInt32 vlc);

        [DllImport("user32.dll")]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        public const int WmHotKey = 0x0312;

        private bool _disposed = false;

        public Key Key { get; private set; }
        public KeyModifier KeyModifiers { get; private set; }
        public Action<HotKey> Action { get; private set; }
        public int Id { get; set; }

        // ******************************************************************
        public HotKey(Key k, KeyModifier keyModifiers, Action<HotKey> action, bool register = true)
        {
            Key = k;
            KeyModifiers = keyModifiers;
            Action = action;
            if (register)
            {
                Register();
            }
        }

        // ******************************************************************
        public bool Register()
        {
            int virtualKeyCode = KeyInterop.VirtualKeyFromKey(Key);
            Id = virtualKeyCode + ((int)KeyModifiers * 0x10000);
            bool result = RegisterHotKey(IntPtr.Zero, Id, (UInt32)KeyModifiers, (UInt32)virtualKeyCode);

            if (_dictHotKeyToCalBackProc == null)
            {
                _dictHotKeyToCalBackProc = new Dictionary<int, HotKey>();
                ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);
            }

            _dictHotKeyToCalBackProc.Add(Id, this);

            Debug.Print(result.ToString() + ", " + Id + ", " + virtualKeyCode);
            return result;
        }

        // ******************************************************************
        public void Unregister()
        {
            HotKey hotKey;
            if (_dictHotKeyToCalBackProc.TryGetValue(Id, out hotKey))
            {
                UnregisterHotKey(IntPtr.Zero, Id);
            }
        }

        // ******************************************************************
        private static void ComponentDispatcherThreadFilterMessage(ref MSG msg, ref bool handled)
        {
            if (!handled)
            {
                if (msg.message == WmHotKey)
                {
                    HotKey hotKey;

                    if (_dictHotKeyToCalBackProc.TryGetValue((int)msg.wParam, out hotKey))
                    {
                        if (hotKey.Action != null)
                        {
                            hotKey.Action.Invoke(hotKey);
                        }
                        handled = true;
                    }
                }
            }
        }

        // ******************************************************************
        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        // ******************************************************************
        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be _disposed.
        // If disposing equals false, the method has been called by the
        // runtime from inside the finalizer and you should not reference
        // other objects. Only unmanaged resources can be _disposed.
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this._disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.
                    Unregister();
                }

                // Note disposing has been done.
                _disposed = true;
            }
        }
    }

    // ******************************************************************
    [Flags]
    public enum KeyModifier
    {
        None = 0x0000,
        Alt = 0x0001,
        Ctrl = 0x0002,
        NoRepeat = 0x4000,
        Shift = 0x0004,
        Win = 0x0008
    }

    // ******************************************************************
}

Si vous envisagez de mélanger Win32 et WPF, voici comment j'ai procédé :

using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows.Media;
using System.Threading;
using System.Windows;
using System.Windows.Input;

namespace GlobalKeyboardHook
{
    public class KeyboardHandler : IDisposable
    {

        public const int WM_HOTKEY = 0x0312;
        public const int VIRTUALKEYCODE_FOR_CAPS_LOCK = 0x14;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vlc);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        private readonly Window _mainWindow;
        WindowInteropHelper _host;

        public KeyboardHandler(Window mainWindow)
        {
            _mainWindow = mainWindow;
            _host = new WindowInteropHelper(_mainWindow);

            SetupHotKey(_host.Handle);
            ComponentDispatcher.ThreadPreprocessMessage += ComponentDispatcher_ThreadPreprocessMessage;
        }

        void ComponentDispatcher_ThreadPreprocessMessage(ref MSG msg, ref bool handled)
        {
            if (msg.message == WM_HOTKEY)
            {
                //Handle hot key kere
            }
        }

        private void SetupHotKey(IntPtr handle)
        {
            RegisterHotKey(handle, GetType().GetHashCode(), 0, VIRTUALKEYCODE_FOR_CAPS_LOCK);
        }

        public void Dispose()
        {
            UnregisterHotKey(_host.Handle, GetType().GetHashCode());
        }
    }
}

Vous pouvez obtenir le code de la clé virtuelle pour le raccourci clavier que vous souhaitez enregistrer ici : http://msdn.microsoft.com/en-us/library/ms927178.aspx

Il existe peut-être un meilleur moyen, mais c'est ce que j'ai jusqu'à présent.

Acclamations!

L'enregistrement des raccourcis au niveau du système d'exploitation n'est presque jamais une bonne chose :les utilisateurs ne veulent pas que vous vous embêtiez avec leur système d'exploitation.

Cela dit, il existe un moyen beaucoup plus simple et convivial de le faire dans WPF, si vous êtes d'accord avec le raccourci clavier fonctionnant uniquement dans l'application (c'est-à-dire tant que votre application WPF a le focus) :

Dans App.xaml.cs :

protected override void OnStartup(StartupEventArgs e)
{
   EventManager.RegisterClassHandler(typeof(Window), Window.PreviewKeyUpEvent, new KeyEventHandler(OnWindowKeyUp));
}

private void OnWindowKeyUp(object source, KeyEventArgs e))
{
   //Do whatever you like with e.Key and Keyboard.Modifiers
}

C'est si simple

Ceci est similaire aux réponses déjà données, mais je trouve cela un peu plus propre :

using System;
using System.Windows.Forms;

namespace GlobalHotkeyExampleForm
{
    public partial class ExampleForm : Form
    {
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

        enum KeyModifier
        {
            None = 0,
            Alt = 1,
            Control = 2,
            Shift = 4,
            WinKey = 8
        }

        public ExampleForm()
        {
            InitializeComponent();

            int id = 0;     // The id of the hotkey. 
            RegisterHotKey(this.Handle, id, (int)KeyModifier.Shift, Keys.A.GetHashCode());       // Register Shift + A as global hotkey. 
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            if (m.Msg == 0x0312)
            {
                /* Note that the three lines below are not needed if you only want to register one hotkey.
                 * The below lines are useful in case you want to register multiple keys, which you can use a switch with the id as argument, or if you want to know which key/modifier was pressed for some particular reason. */

                Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);                  // The key of the hotkey that was pressed.
                KeyModifier modifier = (KeyModifier)((int)m.LParam & 0xFFFF);       // The modifier of the hotkey that was pressed.
                int id = m.WParam.ToInt32();                                        // The id of the hotkey that was pressed.


                MessageBox.Show("Hotkey has been pressed!");
                // do something
            }
        }

        private void ExampleForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            UnregisterHotKey(this.Handle, 0);       // Unregister hotkey with id 0 before closing the form. You might want to call this more than once with different id values if you are planning to register more than one hotkey.
        }
    }
}

je l'ai trouvé sur fluxbytes.com.

Bien que RegisterHotKey soit parfois précisément ce que vous souhaitez, dans la plupart des cas, vous ne souhaitez probablement pas utiliser de raccourcis clavier à l'échelle du système.J'ai fini par utiliser un code comme celui-ci :

using System.Windows;
using System.Windows.Interop;

namespace WpfApp
{
    public partial class MainWindow : Window
    {
        const int WM_KEYUP = 0x0101;

        const int VK_RETURN = 0x0D;
        const int VK_LEFT = 0x25;  

        public MainWindow()
        {
            this.InitializeComponent();

            ComponentDispatcher.ThreadPreprocessMessage += 
                ComponentDispatcher_ThreadPreprocessMessage;
        }

        void ComponentDispatcher_ThreadPreprocessMessage(
            ref MSG msg, ref bool handled)
        {
            if (msg.message == WM_KEYUP)
            {
                if ((int)msg.wParam == VK_RETURN)
                    MessageBox.Show("RETURN was pressed");

                if ((int)msg.wParam == VK_LEFT)
                    MessageBox.Show("LEFT was pressed");
            }
        }
    }
}

Je ne suis pas sûr de WPF, mais cela peut aider.J'ai utilisé la solution décrite dans EnregistrerHotKey (user32) (modifié selon mes besoins bien sûr) pour qu'une application C# Windows Forms attribue une combinaison CTRL-KEY dans Windows pour afficher un formulaire C#, et cela a fonctionné à merveille (même sous Windows Vista).J'espère que cela vous aidera et bonne chance !

j'ai trouvé le Raccourcis clavier globaux dans WPF projet sur codeproject.com qui fait le travail pour moi.Il est relativement récent, n'a pas besoin de référence à System.Windows.Forms et fonctionne "globalement" en termes de réaction à l'appui sur la touche de raccourci même si "votre" application n'est pas la fenêtre active.

RegisterHotKey() suggéré par John pourrait fonctionner - le seul problème est que cela nécessite un HWND (en utilisant PresentationSource.FromVisual(), et conversion du résultat vers un HwndSource).

Cependant, vous devrez également répondre aux WM_HOTKEY message - Je ne sais pas s'il existe un moyen d'accéder ou non au WndProc d'une fenêtre WPF (ce qui peut être fait pour les fenêtres Windows Forms).

La solution de Baboon fonctionne mieux car vous pouvez avoir plusieurs fenêtres.Je l'ai modifié pour qu'il utilise PreviewKeyDownEvent au lieu de PreviewKeyUpEvent afin de gérer la répétition des frappes au clavier.

Je déconseille l'enregistrement au niveau du système d'exploitation, sauf si vous écrivez quelque chose comme un outil de capture ou une application d'enregistrement audio, car cela vous permettra d'accéder aux fonctionnalités lorsque la fenêtre n'est pas ciblée.

Un collègue a écrit un exemple sur la façon de créer un crochet de clavier de bas niveau à utiliser avec WPF.

http://blogs.vertigo.com/personal/ralph/Blog/Lists/Posts/Post.aspx?ID=8

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top