Home > Software design >  C# WPF: How to programmatically press a keyboard button?
C# WPF: How to programmatically press a keyboard button?

Time:04-25

A WPF application has a Test() method that is called when a keyboard shortcut is pressed CTRL G.

The method call works because the string test is printed to the console, from the first line of the method.


The method should programmatically press the key combination CTRL A to select the text in any input field, but this does not happen.

I tried 3 ways:


First: The System.Windows.Forms.SendKeys.SendWait() method, which takes a string, where ^ is CTRL - according to documentation

private void Test(object sender, EventArgs e)
{
    Console.WriteLine("test");
    SendKeys.SendWait("^A");
}

However, there is no pressing.


Second: Implementation via user32.dll, solution taken from here:

[DllImport("user32.dll", SetLastError = true)]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
public static void PressKey(Keys key, bool up)
{
    const int KEYEVENTF_EXTENDEDKEY = 0x1;
    const int KEYEVENTF_KEYUP = 0x2;
    if (up)
        keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, (UIntPtr)0);
    else
        keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY, (UIntPtr)0);
}

private void Test(object sender, EventArgs e)
{
    Console.WriteLine("test");
    PressKey(Keys.ControlKey, up: false);
    PressKey(Keys.A, up: false);
    PressKey(Keys.A, up: true);
    PressKey(Keys.ControlKey, up: true);
}

But in this case, nothing happens.


Third: Installed the package: Install-Package InputSimulator:

private static void Test(object sender, EventArgs e)
{
    Console.WriteLine("test");
    var simu = new InputSimulator();
    simu.Keyboard.ModifiedKeyStroke(VirtualKeyCode.CONTROL, VirtualKeyCode.VK_A);
}

Full code: https://pastebin.com/ay8vRtjA

There are no errors, what am I doing wrong?

CodePudding user response:

The key combination technically works, but the code executes before you have any time to release the ALT key, making the final combination CTRL ALT A instead of CTRL A. I may be overlooking some simpler solution, but the way I found (mostly) works is:

  • Intercept the ALT key
    • If G is pressed while ALT is down, execute the command
    • If G wasn't pressed, allow the keystroke through and send a simulated ALT (so that hotkeys in other applications can still be activated)

It's a hacky workaround and still messes up some regular functionality (for instance if you press a hotkey like ALT A to open a menu, the menu will close as soon as you release ALT), but it makes your hotkey work.

I used a library that I created a couple of years back, called InputHelper, to set up a global keyboard hook to intercept the keystrokes and execute the hotkey. I've yet to publish this to NuGet, so for now you'll have to download it via Releases and add the DLL as a reference in your project.

You'll also need to add a reference to System.Windows.Forms.

using System;
using System.Windows;
using System.Windows.Forms;

namespace HotkeyTest
{
    public partial class MainWindow : Window
    {
        InputHelper.Hooks.KeyboardHook kbHook = new InputHelper.Hooks.KeyboardHook();

        bool AltHotkeyConsumed = false;

        public MainWindow()
        {
            InitializeComponent();

            kbHook.KeyDown  = KeyboardHook_KeyDown;
            kbHook.KeyUp  = KeyboardHook_KeyUp;
        }

        private void KeyboardHook_KeyUp(object sender, InputHelper.EventArgs.KeyboardHookEventArgs e)
        {
            if(e.KeyCode == Keys.LMenu || e.KeyCode == Keys.RMenu)
            {
                if(!AltHotkeyConsumed)
                    // If no hotkey was consumed, press the key again (otherwise it will just be blocked altogether)
                    InputHelper.Keyboard.PressKey(e.KeyCode);
                else
                    AltHotkeyConsumed = false;
            }
        }

        private void KeyboardHook_KeyDown(object sender, InputHelper.EventArgs.KeyboardHookEventArgs e)
        {
            if(e.KeyCode == Keys.LMenu || e.KeyCode == Keys.RMenu)
                e.Block = true;

            if(e.Modifiers == InputHelper.ModifierKeys.Alt)
            {
                if(e.KeyCode == Keys.G)
                {
                    Test();
                    AltHotkeyConsumed = true;
                    e.Block = true;
                }
                else
                {
                    e.Block = false;
                }
            }
        }

        private static void Test()
        {
            InputHelper.Keyboard.SetKeyState(Keys.LControlKey, true);
            InputHelper.Keyboard.PressKey(Keys.A);
            InputHelper.Keyboard.SetKeyState(Keys.LControlKey, false);
        }
    }
}
  • Related