注意:最新版可以在专栏找到
虽然WPF可以通过KeyDown等事件实现快捷方式,但当焦点不位于程序内时(例如游戏全屏),就无法触发了。因此,我们会需要注册全局可用的快捷方式。
目录
Ⅱ 有UI需求,特别是做【设置】界面时,使用【组件】快速构筑界面
方法一 WPF原生方法
HwndSource类,外加自己定义的处理函数(这里则是最下方那个WhileKeyInvoked)
IntPtr WindowhWnd;
//窗口句柄,Windows据此区分打开的各种窗体
Window mainWindow = Application.Current.MainWindow;
WindowhWnd = new WindowInteropHelper(mainWindow).Handle;
//这里是获取主窗口(默认为MainWindow)的句柄
HwndSource source = HwndSource.FromHwnd(WindowhWnd);
source.AddHook(new HwndSourceHook(WhileKeyInvoked));
//这两行就是在消息循环中加了一个处理函数
private IntPtr WhileKeyInvoked(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
//此函数的参数、返回值类型都是固定不变的,函数名可以自己起一个
//它会在接收到系统消息后被触发
}
这种方式固然是OK的,但是,你需要足够了解 uint 值与按键的对应关系,并且需要手写大量的 if 或者 case 来判定是否是你要的值,然后再触发对应的处理函数……这写起来还是不够轻松。
方法二 使用WPF类库【FastHotKeyForWPF】
注意:以下全是【Version 1.1.3】的代码示例,最初的【V1.1.0】演示在最下方B站链接
Ⅰ 低UI需求时,几句话就能注册、修改、删除热键
using FastHotKeyForWPF;
using System.Windows;
namespace TestDemo
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e)
{
GlobalHotKey.Awake();
//激活
GlobalHotKey.Add(ModelKeys.CTRL, NormalKeys.F1, TestA);
//注册CTRL+F1=>TestA
GlobalHotKey.Add(ModelKeys.ALT, NormalKeys.E, TestB);
//注册Alt+E=>TestB
GlobalHotKey.EditHotKey_Keys(TestA, ModelKeys.ALT, NormalKeys.Q);
//修改TestA,使它由组合键Alt+Q触发
GlobalHotKey.EditHotKey_Function(ModelKeys.ALT, NormalKeys.E, TestA);
//修改Alt+E,使它触发函数TestA
GlobalHotKey.DeleteByFunction(TestB);
//删除TestB名下注册的热键
GlobalHotKey.DeleteById(2004);
//删除注册编号为2004的热键
GlobalHotKey.DeleteByKeys(ModelKeys.ALT, NormalKeys.E);
//删除Alt+E注册的热键
BindingRef.BindingAutoEvent(WhileReceiveValue);
//任何TestB这种返回object的无参函数被注册、且被触发时,都会自动触发WhileReceiveValue以做进一步处理
base.OnSourceInitialized(e);
}
protected override void OnClosed(EventArgs e)
{
GlobalHotKey.Destroy();
//销毁
base.OnClosed(e);
}
//被接管的第一个事件
private void TestA()
{
MessageBox.Show("测试A");
}
//被接管的第二个事件
private object TestB()
{
return "测试2";
}
//监测到返回值时发生的事件
private void WhileReceiveValue()
{
MessageBox.Show(BindingRef.Value.ToString());
}
}
}
Ⅱ 有UI需求,特别是做【设置】界面时,使用【组件】快速构筑界面
这里是后端C#部分:
using FastHotKeyForWPF;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace TestDemo
{
public partial class MainWindow : Window
{
static ComponentInfo info = new ComponentInfo(20, Brushes.Black, Brushes.Wheat, new Thickness());
//盒子共用一个基本信息,这里分别是字体大小、颜色/背景色/Margin的属性值
KeySelectBox box1 = PrefabComponent.GetComponent<KeySelectBox>();
KeySelectBox box2 = PrefabComponent.GetComponent<KeySelectBox>();
//两个单键盒子,用于接收用户按下的Key
//V1.1.3 GetComponent<T>修正,只支持Component接口下的类型
KeysSelectBox box3 = PrefabComponent.GetComponent<KeysSelectBox>();
//一个双键盒子,用于接收用户按下的Keys
//这个是V1.1.3 新组件,能同时接收两个用户按下的键
public MainWindow()
{
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e)
{
GlobalHotKey.Awake();
//激活
GlobalHotKey.IsDeBug = true;
//DeBug模式下,部分地方会打印过程值,例如注册成功时会打印一次
Box1.Child = box1;
Box2.Child = box2;
Box3.Child = box3;
//三个Border容器用于填装这些预制组件
box1.UseFatherSize<Border>();
box2.UseFatherSize<Border>();
box3.UseFatherSize<Border>();
//自适应父级容器的大小
//V1.1.3 字体大小也会自适应
box1.UseStyleProperty("MyBox", new string[] { "BorderThickness", "BorderBrush" });
box2.UseStyleProperty("MyBox", new string[] { "BorderThickness", "BorderBrush" });
box3.UseStyleProperty("MyBox", new string[] { "BorderThickness", "BorderBrush" });
//MyBox是前端XAML定义的资源样式,这个函数可以应用指定名称的属性,当然,不指定后面的string[]就是全部应用
//V1.1.3 之前,不能指定属性,而且会覆盖其它内容,如Trigger器
box1.UseFocusTrigger(WhileEnter, WhileLeave);
box2.UseFocusTrigger(WhileEnter, WhileLeave);
box3.UseFocusTrigger(WhileEnter, WhileLeave);
//设置鼠标进出控件时发生的事件,是V1.1.3 新增功能之一,这里默认都是一个样子
BindingRef.Connect(box1, box2, TestA);
//box1、box2 将接管热键组【 box1 + box2 => TestA 】
BindingRef.Connect(box3, TestB);
//box3 将接管热键组【 box3 => TestB 】
BindingRef.BindingAutoEvent(WhileReceiveValue);
//BindingRef监测到任何具备返回值的热键处理函数(如TestB)被触发后,都将自动触发WhileReceiveValue()
PrefabComponent.ProtectSelectBox<KeySelectBox>();
PrefabComponent.ProtectSelectBox<KeysSelectBox>();
PrefabComponent.UnProtectSelectBox<KeySelectBox>();
PrefabComponent.UnProtectSelectBox<KeysSelectBox>();
box1.Protect();
box1.UnProtect();
box3.Protect();
box3.UnProtect();
//V1.1.3 新增Box的锁定功能,PrefabComponent提供了全局保护,每个组件也拥有自己的保护方法
//全局保护的优先级高
base.OnSourceInitialized(e);
}
protected override void OnClosed(EventArgs e)
{
GlobalHotKey.Destroy();
//销毁
base.OnClosed(e);
}
//鼠标进入时发生事件
private void WhileEnter(TextBox box)
{
box.Foreground = Brushes.Cyan;
box.Background = Brushes.Black;
}
//鼠标离开时发生事件
private void WhileLeave(TextBox box)
{
box.Foreground = Brushes.Black;
box.Background = Brushes.Wheat;
}
//被接管的第一个事件
private void TestA()
{
MessageBox.Show("测试A");
}
//被接管的第二个事件
private object TestB()
{
return "测试2";
}
//监测到返回值时发生的事件
private void WhileReceiveValue()
{
MessageBox.Show(BindingRef.Value.ToString());
}
}
}
然后是前端XAML部分
<Window x:Class="TestDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestDemo"
mc:Ignorable="d"
Title="MainWindow" Height="470" Width="800">
<Window.Resources>
<!--这是你自定义的资源样式,后续可以为组件使用样式中定义的属性-->
<Style x:Key="MyBox" TargetType="TextBox">
<Setter Property="Background" Value="Wheat"></Setter>
<Setter Property="FontSize" Value="10"></Setter>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Red"/>
</Style>
</Window.Resources>
<Viewbox>
<Grid Height="450" Width="800">
<!--这两个Border是用来给预制组件KeySelectBox定位的-->
<Border x:Name="Box1" Margin="37,120,540,280" Height="50"/>
<Border x:Name="Box2" Margin="297,120,280,280" Height="50"/>
<!--这个Border是用来给预制组件KeysSelectBox定位的,它能同时接收两个Key,自然也只需要一个父级容器-->
<Border x:Name="Box3" Margin="37,284,280,116" Height="50"/>
</Grid>
</Viewbox>
</Window>
做完上面的事情后,你将实现如下效果
上方的【LeftCtrl + A】所对应的两个Box,就是我们刚才定义的box1和box2,它们被 BindingRef.Connect()后,就能根据接收到的用户按键,自动地管理TestA函数由谁触发了!
然后是下方的Box
是的,这就已经注册完了!当你在任何其它地方按下CTRL+C都能触发你自定义的函数TestB了!
并且当你传给这些Box其它Key时,会自动修改热键的触发方式,算是全权接管热键操作了呢。
必读事项
1.这个类库将作为我在大学期间的试验田,以期望将一些思路与技术付诸实践,因此它的更新可能会比较频繁,但是也请不要担心,核心功能函数的调用方式将尽力维持不变。
2.项目目前在NuGet包管理器就可以直接下载,GitHub与Gitee都有仓库,但由于作者只有一个人,要同时维护专栏+文档+视频,所以【项目>文档>专栏>视频】,可以在专栏找到最新版的介绍!
3.希望大家可以尝试用一下这个类库,你们的反馈才是我最宝贵的收获!(仓库文档有写我的联系方式,当然,直接一点可以在B站私信我)
前往B站观看演示视频 默认是【Version 1.1.0】的介绍视频,但合集内会跟进新版本。