WPF如何注册全局热键?几句话就能搞定!

注意:最新版可以在专栏找到

虽然WPF可以通过KeyDown等事件实现快捷方式,但当焦点不位于程序内时(例如游戏全屏),就无法触发了。因此,我们会需要注册全局可用的快捷方式。

目录

方法一  WPF原生方法

方法二  使用WPF类库【FastHotKeyForWPF】

Ⅰ 低UI需求时,几句话就能注册、修改、删除热键

Ⅱ 有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】的介绍视频,但合集内会跟进新版本。

前往github查阅文档

前往gitee查阅文档

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值