How can I register a global hot key to say CTRL+SH

2019-01-01 10:52发布

I'm building an application in C# using WPF. How can I bind to some keys?

Also, how can I bind to the Windows key?

11条回答
查无此人
2楼-- · 2019-01-01 11:13

If you're going to mix Win32 and WPF, here's how I did it:

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

You can get the virtual-key code for the hotkey you want to register here: http://msdn.microsoft.com/en-us/library/ms927178.aspx

There may be a better way, but this is what I've got so far.

Cheers!

查看更多
若你有天会懂
3楼-- · 2019-01-01 11:13

I'm not sure about WPF, but this may help. I used the solution described in RegisterHotKey (user32) (modified to my needs of course) for a C# Windows Forms application to assign a CTRL-KEY combination within Windows to bring up a C# form, and it worked beautifully (even on Windows Vista). I hope it helps and good luck!

查看更多
忆尘夕之涩
4楼-- · 2019-01-01 11:14

I've found the Global Hotkeys in WPF project on codeproject.com which does the job for me. It's relatively recent, does not need a reference to System.Windows.Forms and works "globally" in terms of reacting to the hotkey being pressed even if "your" application is not the active window.

查看更多
不流泪的眼
5楼-- · 2019-01-01 11:15

Registering OS level shortcuts is hardly ever a good thing: users don't want you to mess with their OS.

That said, there is a much simpler and user friendly way of doing this in WPF, if you're ok with the hotkey working within the application only (i.e as long as your WPF app has the focus):

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

It's that simple

查看更多
情到深处是孤独
6楼-- · 2019-01-01 11:16

Although RegisterHotKey is sometimes precisely what you want, in most cases you probably do not want to use system-wide hotkeys. I ended up using code like the following:

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");
            }
        }
    }
}
查看更多
旧时光的记忆
7楼-- · 2019-01-01 11:19

This is a full working solution, hope it helps...

Usage:

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

...

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

Class:

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
    }

    // ******************************************************************
}
查看更多
登录 后发表回答