【问题标题】:configurable hotkeys for button presses c#用于按钮按下的可配置热键 c#
【发布时间】:2019-03-19 18:34:44
【问题描述】:

我正在寻找一种方法,以便使用我的程序的用户可以自己更改热键,这样它就不会绑定到 D、F12、K、F、A 等等。我有点希望 ppl 可以通过文本框更改它或者可能通过设置文件。已经坚持了几个星期了,我只是找不到实现它的方法,这是我正在处理的第一个程序。

    public void gHook_KeyDown(object sender, KeyEventArgs e)
{
    switch (e.KeyCode)
    {
        case Keys.D:
            // stuff
            Salvagebtn.PerformClick();
            break;
        case Keys.F12:
            // stuff
            pausebtn.PerformClick();
            break;
        case Keys.K:
            //stuff
            Geardropbtn.PerformClick();
            break;
        case Keys.F:
            //stuff
            Gamblebtn.PerformClick();
            break;
        case Keys.A:
            //stuff
            LeftClickSpambtn.PerformClick();
            break;
        case Keys.H:
            // stuff
            openGRbtn.PerformClick();
            break;
        case Keys.B:
            //stuff                    
            gemupbtn.PerformClick();
            break;
    }
}

【问题讨论】:

  • Keys 值的数组(或 Dictionary)替换文字 Keys 值,该值由 enum 索引,表示每个键的命令/用途,例如enum { PauseKey, GearDropKey, GambleKey } 并从文件中读取集合并为值添加编辑器。

标签: c#


【解决方案1】:

在广告期间,我有几分钟的时间。所以,我写了以下内容:

编辑:使用全局键盘处理程序包装的代码。您还需要添加对 PresentationCore 和 WindowsBase 的引用...

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Input;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("user32.dll")]
        static extern int MapVirtualKey(uint uCode, uint uMapType);

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();

        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100,
                          WM_KEYUP = 0x0101,
                          S_WM_KEYDOWN = 0x0104,
                          S_WM_KEYUP = 0x0105;

        private static LowLevelKeyboardProc _proc = HookCallback;
        private static IntPtr _hookID = IntPtr.Zero;

        private static Form1 hookForm;

        private static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

        static bool bShortcutPressed;

        private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (hookForm.Handle != GetForegroundWindow())
            {
                if (nCode >= 0 && ((wParam == (IntPtr)WM_KEYDOWN) || (wParam == (IntPtr)S_WM_KEYDOWN)))
                {
                    int vkCode = Marshal.ReadInt32(lParam);

                    bool bCtrl = (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl));
                    bool bAlt = (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt));
                    bool bShift = (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift));

                    Keys hookKey = (Keys)vkCode;

                    hookKey = (bCtrl) ? ((Keys.Control | hookKey)) : hookKey;
                    hookKey = (bAlt) ? ((Keys.Alt | hookKey)) : hookKey;
                    hookKey = (bShift) ? ((Keys.Shift | hookKey)) : hookKey;

                    Debug.Print($"hookKey {hookKey} {bCtrl} {bAlt} {bShift}");

                    if (!bShortcutPressed && dicTest.ContainsValue(hookKey))
                    {
                        hookForm.OnKeyDown(new System.Windows.Forms.KeyEventArgs(hookKey));
                        bShortcutPressed = true; Debug.Print($"{bShortcutPressed}");
                    }
                }

                if (nCode >= 0 && (((wParam == (IntPtr)WM_KEYUP) || (wParam == (IntPtr)S_WM_KEYUP)))) { bShortcutPressed = false; Debug.Print($"{bShortcutPressed}"); }
            }

            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

        static Dictionary<string, Keys> dicTest = new Dictionary<string, Keys>();

        public void AddHotKey(Action function, Keys key, bool ctrl = false, bool shift = false, bool alt = false)
        {
            KeyDown += delegate (object sender, System.Windows.Forms.KeyEventArgs e) { if (IsHotkey(e, key, ctrl, shift, alt)) { function(); } };
        }

        public bool IsHotkey(System.Windows.Forms.KeyEventArgs eventData, Keys key, bool ctrl = false, bool shift = false, bool alt = false) =>
                             eventData.KeyCode == key && eventData.Control == ctrl && eventData.Shift == shift && eventData.Alt == alt;

        public Form1() => InitializeComponent();

        private void Form1_Load(object sender, EventArgs e)
        {
            _hookID = SetHook(_proc);

            hookForm = this;

            KeyPreview = true;

            String[] names = Enum.GetNames(typeof(Keys));

            foreach (string key in names) { comboBox1.Items.Add(key); }

            comboBox1.SelectedItem = comboBox1.Items[0];

            KeyDown += Form1_KeyDown;
        }

        private void Form1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            Debug.Print($"Form1_KeyDown : {e.KeyData}");
        }

        protected override void OnFormClosing(FormClosingEventArgs e) => UnhookWindowsHookEx(_hookID);

        private void button1_Click(object sender, EventArgs e)
        {
            Keys hookKey = (Keys)Enum.Parse(typeof(Keys), comboBox1.Text);

            hookKey = (checkBox1.Checked) ? ((Keys.Control | hookKey)) : hookKey;
            hookKey = (checkBox2.Checked) ? ((Keys.Alt | hookKey)) : hookKey;
            hookKey = (checkBox3.Checked) ? ((Keys.Shift | hookKey)) : hookKey;

            if (!dicTest.ContainsValue(hookKey))
            {
                Debug.Print($"Going to add : {hookKey} : to our Shortcut Dictionary<>");

                dicTest.Add("test", hookKey);

                AddHotKey(() => { button2.PerformClick(); }, (Keys)new KeysConverter().ConvertFrom(comboBox1.Text), checkBox1.Checked, checkBox3.Checked, checkBox2.Checked);

                //checkBox1.Enabled = checkBox2.Enabled = checkBox3.Enabled = comboBox1.Enabled = button1.Enabled = false;

                label2.Text = "Go ahead and tryout your Shortcut now ...";
            }
            else { label2.Text = "Shortcut key is already stored in our Dictionary<> ..."; }
        }

        private void button2_Click(object sender, EventArgs e) { Debug.Print($"Button2 was clicked"); }
    }
}

您会看到:

尝试所需的快捷方式:

只是一个例子,说明做事有很多方法。

很容易将它们写入文件或其他东西。如果您打算...我会将 AddHotKey Action 参数更改为包含函数名称的字符串,然后使用不同的方法可以让它调用该函数。

真的,我会合并一个字典,如下所述。

【讨论】:

  • 看起来到目前为止我有 2 个关于它的问题......第一个。而不是 Messagebox.show 事件,我尝试做Button2.Performclick 没有那样工作。第二,我希望这些热键在程序在后台运行时工作,我使用 GlobalKeyboardhook 作为我首先使用的代码,但没有让它与热键一起工作。但除此之外,这看起来就是我想要的东西。抱歉,如果有任何问题是愚蠢的,我还是学习 c# 的新手:D
  • 你应该可以在里面放任何代码。它只是一个作为 lambda 发送的动作。我在表单上添加了一个按钮并将 lambda 更改为: () => { button2.PerformClick();如果您的全局键盘挂钩正在工作...您的快捷方式应该正在执行。不知道那里发生了什么。
  • 好吧,只是我太傻了:D 我现在唯一需要的是在按下设置按钮后保存热键并在程序启动时将其加载回来:D
  • 看来我的键盘钩子无法使用该功能将不得不搜索另一种方法:D 但非常感谢您的帮助。
  • 哦...你的钩子正在获取密钥。您必须在 Hook 过程中添加代码以确定它是否是快捷方式,然后执行任何操作。我稍后会为您处理。
【解决方案2】:

您可以通过多种不同的方式来处理这个问题。就个人而言,我会选择使用Dictionary,其中 Keycode 作为 Key,委托/操作作为值。然后在按键上,您只需调用与该键码相关联的字典中的函数。大致如下:

Dictionary<KeyCode, System.Action> keyDict = new Dictionary<KeyCode, System.Action>();

// populate it however you wish. Whether it's user entry or from a file or something.
keyDict.Add(KeyCode.D, SomeFunction);

如果要更改字典中的委托:

keyDict[KeyCode.D] = SomeOtherFunction;

然后在事件处理程序中:

public void gHook_KeyDown(object sender, KeyEventArgs e)
{
    keyDict[e.KeyCode].Invoke();
}

public void SomeFunction()
{
    Console.WriteLine("called some function");
}

(如果需要,这个问题可能会更详细一点:Create dictionary with dynamic keys in C#

【讨论】:

  • 我喜欢你所做的......但是,他希望用户能够自己定义组合键,无论是在 UI 中还是存储在文件中。我的意思是,是的,您可以在必须执行此操作的基础上添加代码……对于像我这样的人来说,这将是一个很好的起点。
  • 这更能解决问题中的直接问题;使键绑定动态而不是硬编码。将它们存储在文件中或手动用户输入完全是“另一个怪物”。
  • 哦,绝对......你绝对正确。 & 就像我说的,我喜欢你所做的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-08-01
  • 2016-04-17
  • 2021-01-27
  • 1970-01-01
  • 2014-05-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多