65.9K
CodeProject 正在变化。 阅读更多。
Home

为WPF添加快捷键的简单代码

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (2投票s)

2018年11月13日

CPOL

1分钟阅读

viewsIcon

6736

为WPF添加快捷键的简单代码

引言

我在开发一个需要快捷键支持的应用程序时,发现技术上可以实现,但没有看到非常简洁的解决方案,所以我自己编写了一个。这段代码提供了一种非常简单的方法,可以将一段代码片段附加/分离到WPF应用程序中的快捷键上。

背景

快捷键是早期Windows版本的遗留物,因此我们需要使用互操作功能来访问它。所有这些都抽象在HotKeyHelper类中,但是关键在于获取WPF应用程序主窗口的遗留窗口句柄(hwnd)。hwnd在构造时不可用,因此我们需要挂钩一个在已知句柄时发生的事件。

Using the Code

快捷键代码被实现为“发射后即忘”,因此您可以添加快捷键而无需显式删除它,但如果需要也可以删除。正如我在背景中提到的,有必要在窗口具有有效的hwnd 我们可以访问时创建HotKeyHelperOnSourceInitialized是一个不错的选择。

  HotKeyHelper _hotKeys;
  int _throwConfettiKeyId;

  protected override void OnSourceInitialized(EventArgs e)
  {
     base.OnSourceInitialized(e);
     _hotKeys = new HotKeyHelper(this);

    // Assign Ctrl-Alt-C to our ThrowConfetti() method. 
    _throwConfettiKeyId = _hotKeys.ListenForHotKey(
        Key.C,
        HotKeyModifiers.Alt | HotKeyModifiers.Control,
        () => { this.ThrowConfetti(); } // put any code you want here
    );
  }

  // Key removal is handled implicitly, but you can explicitly remove 
  // a key like this
  void DoSomeStuffLater()
  {
      _hotKeys.StopListeningForHotKey(_throwConfettiKeyId);   
  }  

这是辅助类的实际代码

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Interop;

namespace HotKeyTools
{
    /// <summary>
    /// Simpler way to expose key modifiers
    /// </summary>
    [Flags]
    public enum HotKeyModifiers
    {
        None = 0,
        Alt = 1,            // MOD_ALT
        Control = 2,        // MOD_CONTROL
        Shift = 4,          // MOD_SHIFT
        WindowsKey = 8,     // MOD_WIN
    }

    /// <summary>
    /// A helpful interface for abstracting this
    /// </summary>
    public interface IHotKeyTool : IDisposable
    {
        int ListenForHotKey(System.Windows.Input.Key key, HotKeyModifiers modifiers, Action keyAction);
        void StopListeningForHotKey(int id);
    }

    // --------------------------------------------------------------------------
    /// <summary>
    /// A nice generic class to register multiple hotkeys for your app
    /// </summary>
    // --------------------------------------------------------------------------
    public class HotKeyHelper : IHotKeyTool
    {
        // Required interop declarations for working with hotkeys
        [DllImport("user32", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        protected static extern bool RegisterHotKey(IntPtr hwnd, int id, uint fsModifiers, uint vk);
        [DllImport("user32", SetLastError = true)]
        protected static extern int UnregisterHotKey(IntPtr hwnd, int id);

        protected const int WM_HOTKEY = 0x312;

        /// <summary>
        /// The unique ID to receive hotkey messages
        /// </summary>
        int _idSeed;

        /// <summary>
        /// Handle to the window listening to hotkeys
        /// </summary>
        private IntPtr _windowHandle;

        /// <summary>
        /// Remember what to do with the hot keys
        /// </summary>
        Dictionary<int, Action> _hotKeyActions = new Dictionary<int, Action>();

        // --------------------------------------------------------------------------
        /// <summary>
        /// ctor
        /// </summary>
        // --------------------------------------------------------------------------

        public HotKeyHelper(Window handlerWindow)
        {
            // Create a unique Id seed
            _idSeed = (int)((DateTime.Now.Ticks % 0x60000000) + 0x10000000);

            // Set up the hook to listen for hot keys
            _windowHandle = new WindowInteropHelper(handlerWindow).Handle;
            if(_windowHandle == null)
            {
                throw new ApplicationException("Cannot find window handle. 
                          Try calling this on or after OnSourceInitialized()");
            }
            var source = HwndSource.FromHwnd(_windowHandle);
            source.AddHook(HwndHook);
        }

        // --------------------------------------------------------------------------
        /// <summary>
        /// Listen generally for hotkeys and route to the assigned action
        /// </summary>
        // --------------------------------------------------------------------------
        private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            if (msg == WM_HOTKEY) 
            {
                var hotkeyId = wParam.ToInt32();
                if (_hotKeyActions.ContainsKey(hotkeyId))
                {
                    _hotKeyActions[hotkeyId]();
                    handled = true;
                }
            }
            return IntPtr.Zero;
        }

        // --------------------------------------------------------------------------
        /// <summary>
        /// Assign a key to a specific action. Returns an id to allow you to stop
        /// listening to this key.
        /// </summary>
        // --------------------------------------------------------------------------
        public int ListenForHotKey
           (System.Windows.Input.Key key, HotKeyModifiers modifiers, Action doThis)
        {
            var formsKey = (Keys)KeyInterop.VirtualKeyFromKey(key);

            var hotkeyId = _idSeed++;
            _hotKeyActions[hotkeyId] = doThis;
            RegisterHotKey(_windowHandle, hotkeyId, (uint)modifiers, (uint)formsKey);
            return hotkeyId;
        }

        // --------------------------------------------------------------------------
        /// <summary>
        /// Stop listening for hotkeys. 
        ///     hotkeyId      The id returned from ListenForHotKey
        /// </summary>
        // --------------------------------------------------------------------------
        public void StopListeningForHotKey(int hotkeyId)
        {
            UnregisterHotKey(_windowHandle, hotkeyId);
        }

        // --------------------------------------------------------------------------
        /// <summary>
        /// Dispose - automatically clean up the hotkey assignments
        /// </summary>
        // --------------------------------------------------------------------------
        public void Dispose()
        {
            foreach(var hotkeyId in _hotKeyActions.Keys)
            {
                StopListeningForHotKey(hotkeyId);
            }
        }
    }
}

历史

  • 2018年11月13日 - 初始版本
© . All rights reserved.