C# 如何使用 WPF 和 .NET 3.5 注册全局热键来说 CTRL+SHIFT+(LETTER)?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/48935/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 10:01:32  来源:igfitidea点击:

How can I register a global hot key to say CTRL+SHIFT+(LETTER) using WPF and .NET 3.5?

提问by w-ll

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

我正在使用 WPF 在 C# 中构建一个应用程序。如何绑定到某些键?

Also, how can I bind to the Windows key?

另外,如何绑定到Windows 键

采纳答案by Gishu

I'm not sure of what you mean by "global" here, but here it goes (I'm assuming you mean a command at the application level, for example, Save Allthat can be triggered from anywhere by Ctrl+ Shift+ S.)

我不知道你所说的“全球性”是什么在这里,但这里有云(我假设你的意思是在应用程序级别的命令,例如,保存所有可以从任何地方通过触发Ctrl+ Shift+ S)。

You find the global UIElementof your choice, for example, the top level window which is the parent of all the controls where you need this binding. Due to "bubbling" of WPF events, events at child elements will bubble all the way up to the root of the control tree.

您可以找到UIElement您选择的全局,例如,顶级窗口,它是您需要此绑定的所有控件的父级。由于 WPF 事件的“冒泡”,子元素中的事件将一直冒泡到控件树的根。

Now, first you need

现在,首先你需要

  1. to bind the Key-Combo with a Command using an InputBindinglike this
  2. you can then hookup the command to your handler (e.g. code that gets called by SaveAll) via a CommandBinding.
  1. 使用InputBinding这样的命令将键组合与命令绑定
  2. 然后,您可以SaveAll通过CommandBinding.

For the WindowsKey, you use the right Keyenumerated member, Key.LWinor Key.RWin

对于WindowsKey,您使用正确的Key枚举成员,Key.LWinKey.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.
    }

回答by John Virgolino

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!

我不确定 WPF,但这可能会有所帮助。我使用RegisterHotKey (user32) 中描述的解决方案当然根据我的需要修改)用于 C# Windows 窗体应用程序在 Windows 中分配一个 CTRL-KEY 组合以显示 C# 窗体,并且它运行得很好(即使在 Windows Vista 上) . 我希望它有所帮助并祝你好运!

回答by Andy

RegisterHotKey()suggested by John could work - the only catch is that it requires an HWND (using PresentationSource.FromVisual(), and casting the result to an HwndSource).

RegisterHotKey()John 建议可以工作 - 唯一的问题是它需要一个 HWND(使用PresentationSource.FromVisual(),并将结果转换为 HwndSource)。

However, you'll also need to respond to the WM_HOTKEYmessage - I'm not sure if there is a way to get access to the WndProc of a WPF window or not (which can be done for Windows Forms windows).

但是,您还需要回复WM_HOTKEY消息 - 我不确定是否有办法访问 WPF 窗口的 WndProc(可以为 Windows 窗体窗口完成)。

回答by Alan Le

A co-worker wrote a sample on how to create a low-level keyboard hook to be used with WPF.

一位同事编写了一个示例,介绍如何创建与 WPF 一起使用的低级键盘挂钩。

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

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

回答by jgraves

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

如果您要混合使用 Win32 和 WPF,我是这样做的:

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

您可以在此处获取要注册的热键的虚拟键代码: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!

干杯!

回答by Mattias Wikstr?m

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:

尽管 RegisterHotKey 有时正是您想要的,但在大多数情况下,您可能不想使用系统范围的热键。我最终使用了如下代码:

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

回答by Eric Ouellet

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
    }

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

回答by Gorgsenegger

I've found the Global Hotkeys in WPFproject 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.

我在 codeproject.com 上找到了WPF项目中的全局热键,它为我完成了这项工作。它是相对较新的,不需要对 System.Windows.Forms 的引用,并且即使“您的”应用程序不是活动窗口,也可以“全局”地对按下的热键做出反应。

回答by Louis Kottmann

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):

也就是说,在 WPF 中有一种更简单且用户友好的方式来执行此操作,如果您只在应用程序中使用热键(即,只要您的 WPF 应用程序具有焦点):

In App.xaml.cs :

在 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

就这么简单

回答by Mario

Baboon's solution works best because you may have multiple windows. I did tweak it so it uses the PreviewKeyDownEvent instead of the PreviewKeyUpEvent in order to handle repetition in keystrokes.

Baboon 的解决方案效果最好,因为您可能有多个窗口。我确实调整了它,因此它使用 PreviewKeyDownEvent 而不是 PreviewKeyUpEvent 来处理击键中的重复。

I would advise against OS-level registration unless you are writing something like a snipping tool or an audio recording app as it will let you access functionality when the window is not focused.

我建议不要进行操作系统级别的注册,除非您正在编写诸如截图工具或录音应用程序之类的东西,因为它可以让您在窗口未聚焦时访问功能。