WPF之全局快捷键

目录

1、WPF快捷键实现方式

2、全局快捷键设置界面

3、Windows API调用

4、注册全局快捷键

5、快捷键触发

WPF快捷键实现方式

WPF快捷键实现主要有自定义快捷键命令和全局快捷键两种方式。

自定义快捷键命令方式是通过KeyBinding为命令绑定快捷键,按键组合可使用“+”进行连接。可以通过Modifiers+Key和Gesture两种方式定义快捷键组合。可以任选其一进行使用,MSDN中建议使用Gesture方式定义以免发生混淆。

  <Window.InputBindings>
        <KeyBinding Modifiers="Control+Alt" Key="Z" Command="{StaticResource CaptureScreen}" />
        <KeyBinding Gesture="Control+Alt+Q" Command="{StaticResource FullScreen}" />
  </Window.InputBindings>

全局快捷键方式是通过调用Windows API的RegisterHotKey函数来实现全局快捷键注册,调用UnregisterHotKey函数实现全局快捷键注销。这种方式WinForm和WPF通用。和自定义命令方式不同的是这种方式是在系统范围内定义热键,而前者是在窗口范围内定义。窗口范围内定义的快捷键触发条件不仅要求窗口可见,并且要求窗口获取键盘焦点。这里引入的问题是,如果命令的目标不具备获取键盘焦点的能力,则命令将会无效。并且,和系统范围内定义的快捷键相冲突时,优先级要低。

如果是Ribbon界面菜单,推荐使用自定义快捷键命令的方式。通过CanExecute方法控制当前命令在目标元素上是否可用,目标元素显示可用或禁用状态。如果是窗口无焦点下触发快捷键,则只能选用全局快捷键方式了。

全局快捷键设置界面

以下是热键设置的界面。接下来对全局快捷键的实现分步骤说明。

这是XAML页面的代码,这里有界面元素的定义。

...... 
    <ItemsControl Margin="10" ItemsSource="{Binding HotKeyList,ElementName=win}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Grid Margin="7">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition Width="Auto" />
                            </Grid.ColumnDefinitions>
                            <CheckBox Grid.Column="0"
                                      Content="{Binding Name}"
                                      IsChecked="{Binding IsUsable}"
                                      Style="{StaticResource ckbStyle1}" />
                            <CheckBox  Grid.Column="1"
                                       Content="Ctrl"
                                       IsChecked="{Binding IsSelectCtrl}"
                                       IsEnabled="{Binding IsUsable}"
                                       Style="{StaticResource ckbStyle2}" />
                            <CheckBox  Grid.Column="2"
                                       Content="Shift"
                                       IsChecked="{Binding IsSelectShift}"
                                       IsEnabled="{Binding IsUsable}"
                                       Style="{StaticResource ckbStyle2}" />
                            <CheckBox  Grid.Column="3"
                                       Content="Alt"
                                       IsChecked="{Binding IsSelectAlt}"
                                       IsEnabled="{Binding IsUsable}"
                                       Style="{StaticResource ckbStyle2}" />
                            <ComboBox  Grid.Column="4"
                                       ItemsSource="{Binding Keys}"
                                       SelectedItem="{Binding SelectKey}"
                                       IsEnabled="{Binding IsUsable}"
                                       Style="{StaticResource cmbStyle1}" />
                        </Grid>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
    ......

首先,新建一个自定义按键枚举。WinForm中可以使用Keys枚举转换,WPF中Key枚举是不正确的,应该使用system.Windows.Froms.Keys枚举,或者自定义正确的枚举或int常量。因为这里定义的枚举会作为快捷键设置的可选项,可以只定义需要击键。

    /// <summary>     /// 自定义按键枚举
    /// </summary>
    public enum EKey
    {
        Space = 32,
        Left = 37,
        Up = 38,
        Right = 39,
        Down = 40,
        A = 65,
        B = 66,
        C = 67,
        D = 68,
        ......
    }

新建快捷键模型类。在模型中,可以直接将EKey枚举值集合绑定到界面的ComboBox上。

    /// <summary>
    /// 快捷键模型
    /// </summary>
    public class HotKeyModel
    {
        /// <summary>
        /// 设置项名称
        /// </summary>
        public string Name { get; set; } 

        /// <summary>
        /// 设置项快捷键是否可用
        /// </summary>
        public bool IsUsable { get; set; } 

        /// <summary>
        /// 是否勾选Ctrl按键
        /// </summary>
        public bool IsSelectCtrl { get; set; } 

        /// <summary>
        /// 是否勾选Shift按键
        /// </summary>
        public bool IsSelectShift { get; set; }

         /// <summary>
        /// 是否勾选Alt按键
        /// </summary>
        public bool IsSelectAlt { get; set; }

         /// <summary>
        /// 选中的按键
        /// </summary>
        public EKey SelectKey { get; set; } 

        /// <summary>
        /// 快捷键按键集合
        /// </summary>
        public static Array Keys
        {
            get
            {
                return Enum.GetValues(typeof(EKey));
            }
        }
    }

Windows API调用

WM_HOTKEY为热键消息,在用户键入被RegisterHotKey函数注册的热键时发送。该消息将位于队列的最前端,并且与注册了这个热键的进程关联。

RegisterHotKey函数定义一个系统范围内的热键。 参数hWnd是接收热键产生WM_HOTKEY消息的窗口句柄。若该参数null,传递给调用线程的WM_HOTKEY消息必须在消息循环中进行处理。 参数id为定义热键的标识符。调用线程中的其他热键,不能使用同样的标识符。为了避免与其他动态链接库定义的热键冲突,一个DLL必须使用GlobalAddAtom函数获取热键的标识符。 参数fsModifiers是定义为了产生WM_HOTKEY消息而必须与由nVirKey参数定义的键一起按下的键,即Ctrl、Shift和Alt的按键组合。 参数vk是定义热键的虚拟键码,也就是选中的EKey中的按键。 PS:当某键被接下时,系统在所有的热键中寻找匹配者。一旦找到一个匹配的热键,系统将把WM_HOTKEY消息传递给登记了该热键的线程的消息队列。该消息被传送到队列头部,因此它将在下一轮消息循环中被移除。该函数不能将热键同其他线程创建的窗口关联起来。若为一热键定义的击键已被其他热键所定义,则RegisterHotKey函数调用失败。若hWnd参数标识的窗口已用与id参数定义的相同的标识符登记了一个热键,则参数fsModifiers和vk的新值将替代这些参数先前定义的值。

UnregisterHotKey函数释放调用线程先前登记的热键。 参数hWnd与被释放的热键相关的窗口句柄。若热键不与窗口相关,则该参数为null。

GlobalAddAtom函数是向全局原子表添加一个字符串,并返回这个字符串的唯一标识符(原子ATOM)。Win32系统中,为了实现信息共享,系统维护了一张全局原子表,用于保存字符串与之对应的标识符的组合。 参数lpString为一个字符串,这个字符串的长度最大为255字节。返回值为一个short类型的原子。若函数调用失败,则返回值为0。 PS:如果字符串中已经存在于全局原子表中,则返回现有的字符串的原子,并且原子的引用计数加1。与原子相关的字符串不会从内存中删除,直到它的引用计数为零。全局原子不会在应用程序终止时自动删除。每次调用GlobalAddAtom函数,必须相应的调用GlobalDeleteAtom函数删除原子。

GlobalFindAtom函数是在表中搜索全局原子。 参数lpString为一个字符串。找到返回原子,没找到返回0。

GlobalDeleteAtom函数是在表中删除全局原子。 参数nAtom为全局原子。

    /// <summary>
    /// 热键管理器
    /// </summary>
    public class HotKeyManager
    {
        /// <summary>
        /// 热键消息
        /// </summary>
        public const int WM_HOTKEY = 0x312;

        /// <summary>
        /// 注册热键
        /// </summary>
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool RegisterHotKey(IntPtr hWnd, int id, ModifierKeys fsModifuers, int vk);

        /// <summary>
        /// 注销热键
        /// </summary>
        [DllImport("user32.dll", SetLastError = true)]
        public static extern bool UnregisterHotKey(IntPtr hWnd, int id);

         /// <summary>
        /// 向原子表中添加全局原子
        /// </summary>
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern short GlobalAddAtom(string lpString);

        /// <summary>
        /// 在表中搜索全局原子
        /// </summary>
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern short GlobalFindAtom(string lpString); 

        /// <summary>
        /// 在表中删除全局原子
        /// </summary>
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern short GlobalDeleteAtom(string nAtom);
    }

注册全局快捷键 

首先,重载OnSourceInitialized函数,这个事件发生在WPF窗体的资源初始化完成,并且可以通过WindowInteropHelper获得该窗体的句柄用来与Win32交互后。调用HwndSource.FromHwnd方法获取当前窗口句柄,再调用hWndSource.AddHook方法添加接收所有窗口消息的事件处理程序。重载OnContentRendered函数,在控件初始化完成后初始化快捷键。

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            // 获取窗体句柄
            m_Hwnd = new WindowInteropHelper(this).Handle;
            HwndSource hWndSource = HwndSource.FromHwnd(m_Hwnd);
            // 添加处理程序
            if (hWndSource != null) hWndSource.AddHook(WndProc);
        }

        /// <summary>
        /// 所有控件初始化完成后调用
        /// </summary>
        /// <param name="e"></param>
        protected override void OnContentRendered(EventArgs e)
        {
            base.OnContentRendered(e);
            // 注册热键
            InitHotKey();
        }

注册全局快捷键到系统中。

        /// <summary>
        /// 初始化注册快捷键
        /// </summary>
        /// <param name="hotKeyModelList">待注册热键的项</param>
        /// <returns>true:保存快捷键的值;false:弹出设置窗体</returns>
        private bool InitHotKey(ObservableCollection<SysParameterSettingHotKeyModel> hotKeyModelList = null)
        {
            var list = hotKeyModelList ?? SysParameterSettingsManager.Instance.LoadDefaultHotKey();
            // 注册全局快捷键
            string failList = HotKeyHelper.RegisterGlobalHotKey(list, m_Hwnd, out m_HotKeySettings);
            if (string.IsNullOrEmpty(failList))
                return true;

            MessageBoxResult mbResult = MessageBoxWindow.Show("提示", string.Format("无法注册下列快捷键\n\r{0}是否要改变这些快捷键?", failList), MessageBoxButton.YesNo);
            var win = SysParameterSettingsWindow.CreateInstance();
            if (mbResult == MessageBoxResult.Yes)
            {
                win.hotKeySet.IsSelected = true;
                if (!win.IsVisible)
                {
                    win.ShowDialog();
                }
                else
                {
                    win.Activate();
                }
                return false;
            }
            return true;
        }

新建一个热键注册帮助类HotKeyHelper。这里有两个需要注意的地方。第一个是全局原子不会在应用程序终止时自动删除,每次调用GlobalAddAtom函数,必须相应的调用GlobalDeleteAtom函数删除原子。第二是若为一热键定义的击键已被其他热键所定义,则RegisterHotKey函数调用失败,所以每次调用RegisterHotKey函数注册热键时,必须先调用UnregisterHotKey函数注销旧的热键。

    /// <summary>
    /// 热键注册帮助
    /// </summary>
    public class HotKeyHelper
    {
        /// <summary>
        /// 记录快捷键注册项的唯一标识符
        /// </summary>
        private static Dictionary<EHotKeySetting, int> m_HotKeySettingsDic = new Dictionary<EHotKeySetting, int>();

        /// <summary>
        /// 注册系统快捷键
        /// </summary>
        /// <param name="hotKeyModelList">待注册快捷键项</param>
        /// <param name="hwnd">窗口句柄</param>
        /// <param name="hotKeySettingsDic">快捷键注册项的唯一标识符字典</param>
        /// <returns>返回注册失败项的拼接字符串</returns>
        public static string RegisterSystemHotKey(IEnumerable<HotKeyModel> hotKeyModelList, IntPtr hwnd, out Dictionary<EHotKeySetting, int> hotKeySettingsDic)
        {
            string failList = string.Empty;
            foreach (var item in hotKeyModelList)
            {
                if (!RegisterHotKey(item, hwnd))
                {
                    string str = string.Empty;
                    if (item.IsSelectCtrl && !item.IsSelectShift && !item.IsSelectAlt)
                    {
                        str = ModifierKeys.Control.ToString();
                    }
                    else if (!item.IsSelectCtrl && item.IsSelectShift && !item.IsSelectAlt)
                    {
                        str = ModifierKeys.Shift.ToString();
                    }
                    else if (!item.IsSelectCtrl && !item.IsSelectShift && item.IsSelectAlt)
                    {
                        str = ModifierKeys.Alt.ToString();
                    }
                    else if (item.IsSelectCtrl && item.IsSelectShift && !item.IsSelectAlt)
                    {
                        str = string.Format("{0}+{1}", ModifierKeys.Control.ToString(), ModifierKeys.Shift);
                    }
                    else if (item.IsSelectCtrl && !item.IsSelectShift && item.IsSelectAlt)
                    {
                        str = string.Format("{0}+{1}", ModifierKeys.Control.ToString(), ModifierKeys.Alt);
                    }
                    else if (!item.IsSelectCtrl && item.IsSelectShift && item.IsSelectAlt)
                    {
                        str = string.Format("{0}+{1}", ModifierKeys.Shift.ToString(), ModifierKeys.Alt);
                    }
                    else if (item.IsSelectCtrl && item.IsSelectShift && item.IsSelectAlt)
                    {
                        str = string.Format("{0}+{1}+{2}", ModifierKeys.Control.ToString(), ModifierKeys.Shift.ToString(), ModifierKeys.Alt);
                    }
                    str += string.Format("+{0}", item.SelectKey.ToString());
                    str = string.Format("{0} ({1})\n\r", item.Name, str);
                    failList += str;
                }
            }
            hotKeySettingsDic = m_HotKeySettingsDic;
            return failList;
        }

        /// <summary>
        /// 注册热键
        /// </summary>
        /// <param name="hotKeyModel">热键待注册项</param>
        /// <param name="hWnd">窗口句柄</param>
        /// <returns>成功返回true,失败返回false</returns>
        private static bool RegisterHotKey(HotKeyModel hotKeyModel, IntPtr hWnd)
        {
            var fsModifierKey = new ModifierKeys();
            var hotKeySetting = (EHotKeySetting)Enum.Parse(typeof(EHotKeySetting), hotKeyModel.Name); 

            if (!m_HotKeySettingsDic.ContainsKey(hotKeySetting))
            {
                // 全局原子不会在应用程序终止时自动删除。每次调用GlobalAddAtom函数,必须相应的调用GlobalDeleteAtom函数删除原子。
                if (HotKeyManager.GlobalFindAtom(hotKeySetting.ToString()) != 0)
                {                    HotKeyManager.GlobalDeleteAtom(HotKeyManager.GlobalFindAtom(hotKeySetting.ToString()));
                }
                // 获取唯一标识符
                m_HotKeySettingsDic[hotKeySetting] = HotKeyManager.GlobalAddAtom(hotKeySetting.ToString());
            }
            else
            {
                // 注销旧的热键
                HotKeyManager.UnregisterHotKey(hWnd, m_HotKeySettingsDic[hotKeySetting]);
            }

            if (!hotKeyModel.IsUsable)
                return true;

            // 注册热键
            if (hotKeyModel.IsSelectCtrl && !hotKeyModel.IsSelectShift && !hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Control;
            }
            else if (!hotKeyModel.IsSelectCtrl && hotKeyModel.IsSelectShift && !hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Shift;
            }
            else if (!hotKeyModel.IsSelectCtrl && !hotKeyModel.IsSelectShift && hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Alt;
            }
            else if (hotKeyModel.IsSelectCtrl && hotKeyModel.IsSelectShift && !hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Control | ModifierKeys.Shift;
            }
            else if (hotKeyModel.IsSelectCtrl && !hotKeyModel.IsSelectShift && hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Control | ModifierKeys.Alt;
            }
            else if (!hotKeyModel.IsSelectCtrl && hotKeyModel.IsSelectShift && hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Shift | ModifierKeys.Alt;
            }
            else if (hotKeyModel.IsSelectCtrl && hotKeyModel.IsSelectShift && hotKeyModel.IsSelectAlt)
            {
                fsModifierKey = ModifierKeys.Control | ModifierKeys.Shift | ModifierKeys.Alt;
            }
            return HotKeyManager.RegisterHotKey(hWnd, m_HotKeySettingsDic[hotKeySetting], fsModifierKey, (int)hotKeyModel.SelectKey);
        }
    }

快捷键触发

通过判断msg 是否为WM_HOTKEY来判断当前快捷键是否触发。通过附加参数wideParam获得当前快捷键触发的项,进而进行相应处理。另外,当前消息处理完成后需要将handled置为true。

        /// <summary>
        /// 窗体回调函数,接收所有窗体消息的事件处理函数
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="msg">消息</param>
        /// <param name="wideParam">附加参数1</param>
        /// <param name="longParam">附加参数2</param>
        /// <param name="handled">是否处理</param>
        /// <returns>返回句柄</returns>
        private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wideParam, IntPtr longParam, ref bool handled)
        {
            var hotkeySetting = new EHotKeySetting();
            switch (msg)
            {
                case HotKeyManager.WM_HOTKEY:
                    int sid = wideParam.ToInt32();
                    if (sid == m_HotKeySettings[EHotKeySetting.全屏])
                    {
                        hotkeySetting = EHotKeySetting.全屏;
                        //TODO 执行全屏操作
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.截图])
                    {
                        hotkeySetting = EHotKeySetting.截图;
                        //TODO 执行截图操作
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.播放])
                    {
                        hotkeySetting = EHotKeySetting.播放;
                        //TODO ......
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.前进])
                    {
                        hotkeySetting = EHotKeySetting.前进;
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.后退])
                    {
                        hotkeySetting = EHotKeySetting.后退;
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.保存])
                    {
                        hotkeySetting = EHotKeySetting.保存;
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.打开])
                    {
                        hotkeySetting = EHotKeySetting.打开;
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.新建])
                    {
                        hotkeySetting = EHotKeySetting.新建;
                    }
                    else if (sid == m_HotKeySettings[EHotKeySetting.删除])
                    {
                        hotkeySetting = EHotKeySetting.删除;
                    }
                    MessageBox.Show(string.Format("触发【{0}】快捷键", hotkeySetting.ToString()));
                    handled = true;
                    break;
            }
            return IntPtr.Zero;
        }

源码下载                                                                          移步到开始↑

时间: 2024-08-05 19:28:51

WPF之全局快捷键的相关文章

禁用 全局快捷键

在给软件添加快捷键时,经常遇到其它软件或者系统已设置的快捷键,导致功能冲突. HotKey函数 下面介绍一个user32.dll的RegisterHotKey以及UnregisterHotKey热键处理的函数 注册热键 RegisterHotKey function BOOL RegisterHotKey(  HWND hWnd, //响应热键的窗口句柄,如果为空,则注册到调用线程上  Int id, //热键的唯一标识  UINT fsModifiers, //热键的辅助按键  UINT vk

删除 Intel HD Graphics 显卡工具的全局快捷键

Intel的内置显卡工具总是注册全局的快捷键,而且还是常用的(比如 Sublime Text, PyCharm等工具的默认按键绑定),如下: Ctrl + Shift + Up Ctrl + Shift + Down Ctrl + Shift + Left Ctrl + Shift + Right 这些快捷键都是由 igfxHK.exe 注册的,虽然可以在任务栏中点击禁用快捷键,但是由于快捷键是全局注册,其它应用程序仍然不能使用这些快捷键. 虽然使用 taskkill /IM igfxHK.ex

wpf阻止键盘快捷键alt+space,alt+F4

原文:wpf阻止键盘快捷键alt+space,alt+F4 /// <summary>        /// 阻止 alt+f4和alt+space 按键        /// </summary>        /// <param name="e"></param>        protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e)       

WPF获得全局窗体句柄,并响应全局键盘事件

场景 wpf窗体运行后,只能捕获当前Active窗体的按键事件,如果要监听windows全局事件,并对当前窗口事件响应. 第一步:导入Winows API public class Win32 { [DllImport("User32.Dll")] public static extern void SetWindowText(int h, String s); /// <summary> /// 如果函数执行成功,返回值不为0. /// 如果函数执行失败,返回值为0.要得

WPF中设置快捷键

方式1: 窗体中加入资源 <UserControl.Resources>        <RoutedUICommand x:Key="Cut" Text="剪切" />        <RoutedUICommand x:Key="Copy" Text="复制" />        <RoutedUICommand x:Key="Paste" Text="

WPF 键盘全局接收消息

1.========================================================================== 在c#中怎样禁用鼠标左键的使用,其实我们可以通过ImessageFilter接口下的PreFilterMessage方法.Application类的AddMessageFilter方法,RemoveMessageFilter方法和Message结构的Msg属性来禁用鼠标左键.Message结构包装Windows发送的消息,可使用该结构包装消息,

mac 全局快捷键启动应用程序

在Mac中,我们可以通过很多方式来启动应用程序.比如通过鼠标在Finder中双击或者通过Spotlight或者QuickSilver等工具来启动.可是,你有没有想过通过一个快捷键就来启动一个应用程序呢?其实,这个功能并不需要使用第三方软件就可以实现.今天,我们就来介绍一下实现的方法. 1. 运行Automator.在弹出的新建窗口中,选择新建一个"服务". 2. 在编辑页面, 选中"资源库"中的"实用工具",然后在出现的操作中,将"开启

VC/MFC中为程序定义全局快捷键

VC 2010-05-01 18:01:34 阅读287 评论0 字号:大中小 订阅 1.注册快捷键 在初始化函数,如OnInitDialog() 注册快捷键,代码如下: #define HotKeyID1 200 BOOL CDlgCloseProSetup::OnInitDialog() { CDialog::OnInitDialog(); ::RegisterHotKey(m_hWnd, HotKeyID1, NULL, VK_HOME); return TRUE; } RegisterH

WPF中的快捷键(累积中)

1. 显示可选属性, ctrl + J 如上图,当不知道Background的可选择时,可以输入 ctrl + J,系统就会显示所有可选属性