WPF監聽快捷鍵的幾種方式

2023-03-20 12:03:58

呼叫Win32 API(優先順序最高,全域性監聽, 支援最小化失焦等情況)

那麼,假如我要在一個WPF程式監聽CTRL+5按鍵,首先在主視窗程式新增以下程式碼:


        /// <summary>
        /// CTRL+5事件Id
        /// </summary>
        private const int Ctrl5KeyEventId = 9000;


        [DllImport("user32.dll")]
        public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);

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


        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);

            var handle = new WindowInteropHelper(this).Handle;
            var source = HwndSource.FromHwnd(handle);
            source?.AddHook(HwndHook);
            //真正註冊快捷鍵監聽處理: 同時註冊數位鍵和小鍵盤的CTRL+5
            RegisterHotKey(handle, Ctrl5KeyEventId, (uint)ModifierKeys.Control, (uint)KeyInterop.VirtualKeyFromKey(Key.D5));
            RegisterHotKey(handle, Ctrl5KeyEventId, (uint)ModifierKeys.Control, (uint)KeyInterop.VirtualKeyFromKey(Key.NumPad5));
        }


        private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            const int wmHotkey = 0x0312;

            switch (msg)
            {
                case wmHotkey:
                    switch (wParam.ToInt32())
                    {
                        case Ctrl5KeyEventId:
                            Debug.WriteLine("Win32監聽CTRL+5成功");
                            break;
                    }
                    break;
            }

            return IntPtr.Zero;
        }


        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);

            var handle = new WindowInteropHelper(this).Handle;
            //關閉視窗後取消註冊
            UnregisterHotKey(handle, Ctrl5KeyEventId);
        }

監聽WPF的KeyDown事件(不夠清真,可選擇,最小化失焦等情況監聽失效)


        public MainWindow()
        {
            InitializeComponent();
            KeyDown += MainWindow_KeyDown;
        }


        private void MainWindow_KeyDown(object sender, KeyEventArgs e)
        {
            if (Keyboard.Modifiers == ModifierKeys.Control && (e.Key == Key.D5 || e.Key == Key.NumPad5))
            {
                Debug.WriteLine("WPF的KeyDown事件監聽CTRL+5成功"); ;
                e.Handled = true;
            }
        }


XAML繫結命令方式(WPF當然優先選中命令繫結啦,清真,最小化失焦等情況監聽失效)

以下為Window主表單的XAML程式碼


    <Window.CommandBindings>
        <CommandBinding Command="{x:Static local:Commands.Ctrl5Command}" Executed="Ctrl5Command_OnExecuted"/>
    </Window.CommandBindings>
    <Window.InputBindings>
        <KeyBinding Modifiers="Control" Key="D5"  Command="{x:Static  local:Commands.Ctrl5Command}" />
        <KeyBinding Modifiers="Control" Key="NumPad5"  Command="{x:Static  local:Commands.Ctrl5Command}" />
    </Window.InputBindings>


在Window主表單後臺程式碼建立命令對應的Executed方法


        private void Ctrl5Command_OnExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            Debug.WriteLine("WPF的XAML繫結命令監聽CTRL+5成功");
        }

新增命令相關的靜態類:


    public static class Commands
    {
        public static ICommand Ctrl5Command { get; } = new RoutedCommand();
    }

細節

三個監聽方案的優先順序

其中Win32 > XAML繫結命令 = KeyDown事件,假如同時監聽的話,其中會只處理高優先順序的,以上面的例子,假如
我同時監聽三個,只會處理win32的


Win32監聽CTRL+5成功

全域性監聽問題

其中win32支援全域性監聽鍵盤,也就是視窗在失焦情況下,例如最小化,也能監聽得到,其中XAML繫結命令KeyDown事件不支援失焦情況,最小化等情況也就監聽不到了,因此,要按業務選擇方案

DEMO

DEMO連結