博客
关于我
WPF之全局快捷键
阅读量:427 次
发布时间:2019-03-06

本文共 14448 字,大约阅读时间需要 48 分钟。

目录

 

 

WPF快捷键实现方式 


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

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

 

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

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

 

全局快捷键设置界面 


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

 

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

......     
......

 

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

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

 

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

///     /// 快捷键模型    ///     public class HotKeyModel    {        ///         /// 设置项名称        ///         public string Name { get; set; }         ///         /// 设置项快捷键是否可用        ///         public bool IsUsable { get; set; }         ///         /// 是否勾选Ctrl按键        ///         public bool IsSelectCtrl { get; set; }         ///         /// 是否勾选Shift按键        ///         public bool IsSelectShift { get; set; }         ///         /// 是否勾选Alt按键        ///         public bool IsSelectAlt { get; set; }         ///         /// 选中的按键        ///         public EKey SelectKey { get; set; }         ///         /// 快捷键按键集合        ///         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参数定义的键一起按下的键,即CtrlShiftAlt的按键组合。 参数vk是定义热键的虚拟键码,也就是选中的EKey中的按键。 PS:当某键被接下时,系统在所有的热键中寻找匹配者。一旦找到一个匹配的热键,系统将把WM_HOTKEY消息传递给登记了该热键的线程的消息队列。该消息被传送到队列头部,因此它将在下一轮消息循环中被移除。该函数不能将热键同其他线程创建的窗口关联起来。若为一热键定义的击键已被其他热键所定义,则RegisterHotKey函数调用失败。若hWnd参数标识的窗口已用与id参数定义的相同的标识符登记了一个热键,则参数fsModifiersvk的新值将替代这些参数先前定义的值。

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

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

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

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

///     /// 热键管理器    ///     public class HotKeyManager    {        ///         /// 热键消息        ///         public const int WM_HOTKEY = 0x312;        ///         /// 注册热键        ///         [DllImport("user32.dll", SetLastError = true)]        public static extern bool RegisterHotKey(IntPtr hWnd, int id, ModifierKeys fsModifuers, int vk);        ///         /// 注销热键        ///         [DllImport("user32.dll", SetLastError = true)]        public static extern bool UnregisterHotKey(IntPtr hWnd, int id);         ///         /// 向原子表中添加全局原子        ///         [DllImport("kernel32.dll", SetLastError = true)]        public static extern short GlobalAddAtom(string lpString);         ///         /// 在表中搜索全局原子        ///         [DllImport("kernel32.dll", SetLastError = true)]        public static extern short GlobalFindAtom(string lpString);         ///         /// 在表中删除全局原子        ///         [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);        }        ///         /// 所有控件初始化完成后调用        ///         ///         protected override void OnContentRendered(EventArgs e)        {            base.OnContentRendered(e);            // 注册热键            InitHotKey();        }

 

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

///         /// 初始化注册快捷键        ///         /// 待注册热键的项        /// 
true:保存快捷键的值;false:弹出设置窗体
private bool InitHotKey(ObservableCollection
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函数注销旧的热键。 

///     /// 热键注册帮助    ///     public class HotKeyHelper    {        ///         /// 记录快捷键注册项的唯一标识符        ///         private static Dictionary
m_HotKeySettingsDic = new Dictionary
(); ///
/// 注册系统快捷键 /// ///
待注册快捷键项 ///
窗口句柄 ///
快捷键注册项的唯一标识符字典 ///
返回注册失败项的拼接字符串
public static string RegisterSystemHotKey(IEnumerable
hotKeyModelList, IntPtr hwnd, out Dictionary
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; } ///
/// 注册热键 /// ///
热键待注册项 ///
窗口句柄 ///
成功返回true,失败返回false
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

///         /// 窗体回调函数,接收所有窗体消息的事件处理函数        ///         /// 窗口句柄        /// 消息        /// 附加参数1        /// 附加参数2        /// 是否处理        /// 
返回句柄
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; }

 

 

                                                                          

 

转载地址:http://mejyz.baihongyu.com/

你可能感兴趣的文章
83. Remove Duplicates from Sorted List
查看>>
410. Split Array Largest Sum
查看>>
程序员视角:鹿晗公布恋情是如何把微博搞炸的?
查看>>
Spring+SpringMVC+MyBatis+easyUI整合进阶篇(七)一次线上Mysql数据库崩溃事故的记录
查看>>
系统编程-进程间通信-无名管道
查看>>
为什么我觉得需要熟悉vim使用,难道仅仅是为了耍酷?
查看>>
一个支持高网络吞吐量、基于机器性能评分的TCP负载均衡器gobalan
查看>>
HDOJ2017_字符串统计
查看>>
404 Note Found 团队会议纪要
查看>>
使用Redis作为Spring Security OAuth2的token存储
查看>>
【SOLVED】Linux使用sudo到出现输入密码提示延迟时间长
查看>>
springmvc转springboot过程中访问jsp报Whitelabel Error Page错误
查看>>
项目引入非配置的文件,打成war包后测试报错的可能原因
查看>>
Git学习笔记
查看>>
不需要爬虫也能轻松获取 unsplash 上的图片
查看>>
痞子衡嵌入式:语音处理工具pzh-speech诞生记(2)- 界面构建(wxFormBuilder3.8.0)
查看>>
痞子衡嵌入式:极易上手的可视化wxPython GUI构建工具(wxFormBuilder)
查看>>
痞子衡嵌入式:串口调试工具pzh-com诞生记(2)- 界面构建(wxFormBuilder3.8.0)
查看>>
elementUi源码解析(1)--项目结构篇
查看>>
Nmap扫描工具介绍
查看>>