0

Hi my problem seems quite simple but I couldn't solve it alone.

I have a WinForms application and it has a button. When I press it some code runs and I want to have a loop inside it that breaks whenever any key is pressed.

I of course can't use Console.ReadKey() because it's a winforms and I don't want to have to click with the mouse on a button, also I don't want to use another EventArg.

Is there any way to detect a keystroke in a winforms application with a loop.

Here's the idea :

private void btn_Click(object sender, EventArgs e)
    {
        // Do stuff
        while (Key is not pressed)
            { 
                try to detect key pressing
            }
        // Other stuff
    }

Thanks in advance.

Sifflex
  • 51
  • 2
  • 7
  • The answer is going to depend on what is going on with "some code runs". Is the form still receiving updates and the code running asynchronously? – ForeverZer0 Jul 15 '18 at 01:40
  • You're better off having separate button and keyboard handlers and introduce a _flag_ –  Jul 15 '18 at 02:17
  • You can't wait, that will hang the UI. Simply set a bool variable to *true* in the Click event handler. Then in the KeyPress event handler you can check the variable and do the //otherstuff. Do think a bit how it needs to be reset back to *false*. – Hans Passant Jul 15 '18 at 13:14

3 Answers3

2

If you want to detect a key press while the form is focus or not you can use the anwser of this question https://stackoverflow.com/a/18291854/10015658

So your code would like:

Create new class "KeyHandler":

public class KeyHandler
{
    [DllImport("user32.dll")]
    private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);

    [DllImport("user32.dll")]
    private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

    private int key;
    private IntPtr hWnd;
    private int id;

    public KeyHandler(Keys key, Form form)
    {
        this.key = (int)key;
        this.hWnd = form.Handle;
        id = this.GetHashCode();
    }

    public override int GetHashCode()
    {
        return key ^ hWnd.ToInt32();
    }

    public bool Register()
    {
        return RegisterHotKey(hWnd, id, 0, key);
    }

    public bool Unregiser()
    {
        return UnregisterHotKey(hWnd, id);
    }
}

Your main From class: (assume that your form class name is Form1)

private KeyHandler ghk;
bool isButtonClicked = false;

public Form1()
{
    InitializeComponent();

    // Keys.A is the key you want to subscribe
    ghk = new KeyHandler(Keys.A, this);
    ghk.Register();
}

private void HandleHotkey()
{
     // Key 'A' pressed

     if(isButtonClicked)
     {
         // Put the boolean to false to avoid spamming the function with multiple press
         isButtonClicked = false;

         // Other Stuff
     }
}

private void btn_Click(object sender, EventArgs e)
{
    // Do stuff

    isButtonClicked = true;
}

protected override void WndProc(ref Message m)
{
    //0x0312 is the windows message id for hotkey
    if (m.Msg == 0x0312)
        HandleHotkey();
    base.WndProc(ref m);
}

Do Not forget usings:

using System.Windows.Forms;
using System.Runtime.InteropServices;
s0rana
  • 1,060
  • 8
  • 9
0

There are many ways you can acheive what you want.

A simple way is to set Form's KeyPreview to true and handle keypress event on form level. Note that you need to allow a break in your loop (Thread.Sleep() or Task.Delay()).

For advanced hook up to windows message, check this question

Yves Israel
  • 408
  • 3
  • 5
0

One way to go about this is to add an event handler for KeyDown once the button is clicked, let it wait for the key input, then trigger the second action and remove the event handler.

It would look something like this:

private void btn_Click(object sender, EventArgs e)
{
    // Do stuff
    KeyEventHandler handler = null;
    handler = delegate (object s, KeyEventArgs ev)
    {
        if (ev.KeyCode == Keys.A)
        {
            btn.KeyDown -= handler;
            // Do other stuff
        }
    };
    btn.KeyDown += handler;
}

Of course, you'd need to replace Keys.A with the actual key you want to "listen" for.