簡體   English   中英

如何使用 WPF 和 .NET 3.5 注冊全局熱鍵來說 CTRL+SHIFT+(LETTER)?

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

我正在使用 WPF 在 C# 中構建一個應用程序。 如何綁定到某些鍵?

另外,如何綁定到Windows 鍵

這是一個完整的工作解決方案,希望它有所幫助...

用法:

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

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

我不確定你在這里所說的“全局”是什么意思,但它是這樣的(我假設你的意思是應用程序級別的命令,例如,可以通過Ctrl + Shift + S從任何地方觸發的全部保存.)

您可以找到您選擇的全局UIElement ,例如,頂級窗口,它是您需要此綁定的所有控件的父級。 由於 WPF 事件的“冒泡”,子元素中的事件將一直冒泡到控件樹的根。

現在,首先你需要

  1. 使用這樣的InputBinding將 Key-Combo 與 Command 綁定
  2. 然后,您可以通過CommandBinding將命令連接到您的處理程序(例如,由SaveAll調用的SaveAll )。

對於Windows Key,您使用正確的Key枚舉成員Key.LWinKey.RWin

public WindowMain()
{
   InitializeComponent();

   // Bind Key
   var ib = new InputBinding(
       MyAppCommands.SaveAll,
       new KeyGesture(Key.S, ModifierKeys.Shift | ModifierKeys.Control));
   this.InputBindings.Add(ib);

   // Bind handler
   var 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.
}

注冊操作系統級別的快捷方式幾乎不是一件好事:用戶不希望您弄亂他們的操作系統。

也就是說,在 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
}

就這么簡單

如果您要混合使用 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

可能有更好的方法,但這是我到目前為止所得到的。

干杯!

這類似於已經給出的答案,但我覺得它更清晰:

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上找到了它。

我在 codeproject.com 上找到了WPF項目中的 全局熱鍵,它為我完成了這項工作。 它是相對較新的,不需要對 System.Windows.Forms 的引用,並且即使“您的”應用程序不是活動窗口,也可以“全局”地對按下的熱鍵做出反應。

Baboon 的解決方案效果最好,因為您可能有多個窗口。 我確實調整了它,因此它使用 PreviewKeyDownEvent 而不是 PreviewKeyUpEvent 來處理擊鍵中的重復。

我建議不要進行操作系統級別的注冊,除非您正在編寫諸如截圖工具或錄音應用程序之類的東西,因為它可以讓您在窗口未聚焦時訪問功能。

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

我不確定 WPF,但這可能會有所幫助。 我使用RegisterHotKey (user32) 中描述的解決方案當然根據我的需要修改)用於 C# Windows 窗體應用程序在 Windows 中分配一個 CTRL-KEY 組合以顯示 C# 窗體,並且它工作得很好(即使在 Windows Vista 上) . 我希望它有所幫助並祝你好運!

使用 NHotKey 包,您可以將熱鍵設為全局:

簡而言之,對於 XAML,您需要做的就是替換

<KeyBinding Gesture="Ctrl+Alt+Add" Command="{Binding IncrementCommand}" />

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

John 建議的RegisterHotKey()可以工作 - 唯一的問題是它需要一個 HWND(使用PresentationSource.FromVisual() ,並將結果轉換為 HwndSource)。

但是,您還需要響應WM_HOTKEY消息 - 我不確定是否有辦法訪問 WPF 窗口的 WndProc(可以為 Windows 窗體窗口完成)。

一位同事寫了一個示例,說明如何創建與WPF一起使用的低級鍵盤掛鈎。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM