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

我使用WPF在C#中构build应用程序。 我怎样才能绑定到一些键?

另外,我怎样才能绑定到Windows键 ?

我不确定这里的“全局”是什么意思,但是在这里(我假设你是指在应用程序级别的一个命令,例如,可以从任何地方通过按Ctrl + Shift + S 。)

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

现在,首先你需要

  1. 使用InputBinding将Key-Combo与Command绑定在一起
  2. 您可以通过CommandBinding将命令连接到您的处理程序(例如,由SaveAll调用的SaveAll )。

对于Windows Key,您使用正确的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. } 

这是一个完整的工作解决scheme,希望它可以帮助…

用法:

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

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

类:

 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 } // ****************************************************************** } 

如果你打算混合使用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()); } } } 

你可以得到你想在这里注册的热键的虚拟键代码: http : //msdn.microsoft.com/en-us/library/ms927178.aspx

可能有更好的办法,但这是迄今为止我所得到的。

干杯!

注册操作系统级别的快捷键几乎不是一件好事:用户不希望你搞砸他们的操作系统。

也就是说,在WPF中这样做有一个简单和用户友好的方法,如果你只是在应用程序内工作的热键(即只要你的WPF应用程序有焦点):

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

就这么简单

虽然RegisterHotKey有时正是你想要的,但在大多数情况下,你可能不想使用系统级的热键。 我结束了使用如下代码:

使用System.Windows;
使用System.Windows.Interop;

命名空间WpfApp
 {
    公共部分类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(
            参考MSG味精,参考布尔处理)
         {
            如果(msg.message == WM_KEYUP)
             {
                如果((int)msg.wParam == VK_RETURN)
                     MessageBox.Show(“RETURN was pressed”);

                如果((int)msg.wParam == VK_LEFT)
                     MessageBox.Show(“LEFT was pressed”);
             }
         }
     }
 }

我不确定WPF,但这可能有帮助。 我使用了RegisterHotKey(user32)中描述的解决scheme当然修改为我的需要),让C#Windows窗体应用程序在Windows中分配一个CTRL-KEY组合,调出一个C#表单,并且它运行得非常好(即使在Windows Vista上) 。 我希望它有帮助,祝你好运!

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

这与已经给出的答案类似,但我觉得它更清洁一些:

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

我在fluxbytes.com上find了它。

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

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

狒狒的解决scheme效果最好,因为你可能有多个窗口。 我做了调整,所以它使用PreviewKeyDownEvent而不是PreviewKeyUpEvent来处理重复击键。

我build议不要操作系统级别的注册,除非你正在写一些像剪切工具或audio录音应用程序,因为它会让你访问function,当窗口不集中。

一位同事写了一个关于如何创build一个WPF使用的低级键盘钩子的例子。

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