目录

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 = ,
Left = ,
Up = ,
Right = ,
Down = ,
A = ,
B = ,
C = ,
D = ,
......
}

新建快捷键模型类。在模型中,可以直接将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为一个字符串,这个字符串的长度最大为字节。返回值为一个short类型的原子。若函数调用失败,则返回值为。 PS:如果字符串中已经存在于全局原子表中,则返回现有的字符串的原子,并且原子的引用计数加。与原子相关的字符串不会从内存中删除,直到它的引用计数为零。全局原子不会在应用程序终止时自动删除。每次调用GlobalAddAtom函数,必须相应的调用GlobalDeleteAtom函数删除原子。

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

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()) != )
{ 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;
}

源码下载                                                                          移步到开始↑

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

  1. WPF设置全局快捷键

    转自:http://www.cnblogs.com/atskyline/archive/2012/09/20/2694878.html 第一步 引入到Winows API 偷懒直接写在类里 1: [D ...

  2. WPF 注册全局快捷键

    .NET技术交流群 199281001 .欢迎加入. using System; using System.Collections.Generic; using System.Linq; using ...

  3. 在WPF中使用全局快捷键

    今天写一个小程序中使用到了全局快捷键,找到了我之前写的文章在c#中使用全局快捷键翻了一下,发现它是WinForm版本的,而我现在大部分写WPF程序了,便将其翻译了为WPF版本的了. static cl ...

  4. Delphi 中的全局快捷键+给指定窗体发送按键

    [背景] 公司做视频影像采集,平时采集图像的时候都需要打开采集窗口,然后需要开着采集窗口来进行图像采集.同事问我能不能做一个全局快捷键,哪怕我没有操作也可以采集图像.说干就干,一直想做全局快捷键了,网 ...

  5. WPF设置全局字体和字体嵌入

    原文:WPF设置全局字体和字体嵌入 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/CLeopard/article/details/40590373 ...

  6. 在Form Load中设置showInTaskBar =false 或 隐藏窗口 this.Hide()时会导致注册的全局快捷键无效

    在Form Load中设置showInTaskBar =false   或 隐藏窗口 this.Hide() 会导致注册的全局快捷键无效.  反正是其中一个,有点记不清了. 在Form Shown中s ...

  7. wpf绑定全局静态变量(mvvm)

    原文 wpf绑定全局静态变量(mvvm) 在实际的开发中,有一些集合或者属性可能是全局的,比如当你做一个oa的时候,可能需要展示所有的人员,这时这个所有的人员列表显然可以作为全局参数,比如这里有一个全 ...

  8. WPF设置全局控件样式

    原文:WPF设置全局控件样式 方法: 在资源文件APP.XAML中添加如下资源 <Application x:Class="_360UI.App" xmlns="h ...

  9. JavaFx全局快捷键实现(Kotlin)

    原文地址: JavaFx全局快捷键实现(Kotlin) | Stars-One的杂货小窝 最近整款工具需要用到全局快捷键,搜集了下网上的资料,发现有个JIntellitype库可以用来实现全局快捷键, ...

随机推荐

  1. 【转】MySQL的Replace into 与Insert into on duplicate key update真正的不同之处

    原文链接:http://www.jb51.net/article/47090.htm   今天听同事介绍oracle到mysql的数据migration,他用了Insert into ..... on ...

  2. display---我的第一篇博客

    display:block显示为块级元素,换行 display:inline-block显示为块级内联元素,不换行,但是可以改变其宽度高度 display:inline显示为内联元素,不换行但是也不能 ...

  3. 功能齐全的图表库 ACharts

    ACharts是基于Raphael 库开发的,而Raphael.js是基于svg和vml语言,因此最低可以兼容到IE6+,而最高则所有支持w3c svg标准的现代浏览器都可以使用,svg甚至在手机平台 ...

  4. Web 前沿——HTML5 Form Data 对象的使用

    XMLHttpRequest Level 2 添加了一个新的接口——FormData.利用 FormData 对象,我们可以通过 JavaScript 用一些键值对来模拟一系列表单控件,我们还可以使用 ...

  5. 2016京东Android研发校招笔试题

    一.选择题汇总,具体的记不住啦.. 1.计网:ip的网络前缀.SNMP(报文组成):http://blog.csdn.net/shanzhizi/article/details/11606767 参考 ...

  6. NSURLSession从网络上下载资源,此程序下载的是视频

    #import "ViewController.h" @interface ViewController ()<NSURLSessionDelegate, NSURLSess ...

  7. View的事件体系

    View的滑动 实现手段 优点 缺点 备注 scrollTo/scrollBy 使用简单 只能滑动view的内容,并不会滑动view本身. 且内容超出view本身的布局范围部分的不会显示 不适合有交互 ...

  8. XML解析之SAX详解

    XML解析之SAX详解 本文属于作者原创 http://www.cnblogs.com/ldnh/ XML解析的五个步骤 1.打开文档 (void)parserDidStartDocument:(NS ...

  9. iOS开发之UIAlertView与UIAlertController的详尽用法说明

    本文将从四个方面对IOS开发中UIAlertView与UIAlertController的用法进行讲解: 一.UIAlertView与UIAlertController是什么东东? 二.我们为什么要用 ...

  10. VisualSVN Server的配置和使用方法 图文

    转载 http://www.jb51.net/article/17365.htm VisualSVN Server是免费的,而VisualSVN是收费的.VisualSVN是SVN的客户端,和Visu ...