1

I am using C# in the .Net Framework 8.0. I want to escape while loop using Keyboard input.

At below C# code,

1) If I remove " MessageBox.Show(State.ToString()); ", I can't escape the while loop.

2) If I add "MessageBox.Show(State.ToString());", I can escape the while loop. But, it's not perfect. Because after I see the MessageBox " false ", I must press "A" and Enter. Then Next Step, State is changed to "true". And I can see "true" > " Entered !!! " > "While Loop is Broken !!!" MessageBox.

a) What is the difference 1) and 2).

b) How can I escape while loop using keyboard input? Easily.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Windows.Input;

namespace WF_Templete_
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void bt_Start_Click(object sender, EventArgs e)
        {
            while (true)
            {
                bool State = (System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.A) == true);
                // MessageBox.Show(State.ToString());

                if (State)
                {
                    MessageBox.Show("Entered !");
                    break;
                }
            }
            MessageBox.Show("While Loop is Broken !!!");
        }

    }
}
Cid
  • 14,968
  • 4
  • 30
  • 45
  • 1
    There is no .Net-framework 8, but C# 8. The framework may be 4.7.1 for example. – MakePeaceGreatAgain Sep 27 '19 at 09:54
  • note that `bool State = (System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.A) == true);` can be simplified to `bool State = Keyboard.IsKeyDown(Key.A);` since you already included `System.Windows.Input` and `bool State = true/false == true` is redundant – Cid Sep 27 '19 at 09:58
  • 1
    This is an X-Y problem. What are you actually trying to achieve? – Matthew Watson Sep 27 '19 at 10:07
  • Thanks your comments. Anyway, Importance thing is while( Keyboard.IsKeyDown(Key.A)) or if (Keyboard.IsKeyDown(Key.A)) are not work. – jaebeom lim Sep 27 '19 at 10:12
  • But what are you trying to do? Run some background thread until `A` is pressed without blocking the UI? The correct answer will depend on what you're actually trying to do, rather than tying to fix your incorrect solution to the whatever that problem is... – Matthew Watson Sep 27 '19 at 10:14
  • using the while loop, I wait the real button signal using USB port. If real button is pressed, I want to see " Button is pressed". And if computer "a" button is pressed, I want to stop the waiting of the real button signal. – jaebeom lim Sep 27 '19 at 10:28

2 Answers2

1

The simplest solution is to add in Application.DoEvents() in to the loop. This allows your code to process events that would otherwise be ignored during a tight loop.

Ideally you would want to run your business logic outside of the UI thread using a MVC or MVVM pattern, which would then leave you with a responsive UI that can be interacted with whilst back end processing is performed.

            while (true)
            {
                Application.DoEvents();
                bool State = (System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.A) == true);
                // MessageBox.Show(State.ToString());

                if (State)
                {
                    MessageBox.Show("Entered !");
                    break;
                }
            }
Murray Foxcroft
  • 12,785
  • 7
  • 58
  • 86
  • Never use `Application.DoEvents()`. [It is evil.](https://blog.codinghorror.com/is-doevents-evil/) – Matthew Watson Sep 27 '19 at 10:03
  • On the contrary, but what do you want to use then? –  Sep 27 '19 at 10:04
  • 1
    Murray Foxcroft) Thanks. // Matthew Watson) What is the disadvantage of Application.DoEvents() ? – jaebeom lim Sep 27 '19 at 10:33
  • @jaebeomlim `Application.DoEvents()` can have all sorts of nasty side-effects, including unanticipated re-enterant calls. There's lots of discussion about it around the 'net, e.g. https://stackoverflow.com/questions/5181777/use-of-application-doevents – Matthew Watson Sep 27 '19 at 10:45
  • If you are doing a school project or just some quick and easy code (PoC / Demo), it's fine. If you are running production code, then avoid. – Murray Foxcroft Sep 27 '19 at 10:59
0

From your comments, it looks like you want to be able to start waiting for an external USB button press, or for a cancellation key on the keyboard to be pressed.

One way to approach this is to encapsulate the waiting for USB button or cancellation in a class like so:

public sealed class UsbButtonWaiter
{
    public UsbButtonWaiter(CancellationToken cancellation)
    {
        _cancellation = cancellation;
    }

    public async Task<bool> WaitForUsbButtonAsync()
    {
        var waitForUsbButton    = Task.Run(this.waitForUsbButton);
        var waitForCancellation = new Task(() => throw new InvalidOperationException(), _cancellation);

        return await Task.WhenAny(waitForUsbButton, waitForCancellation) == waitForUsbButton;
    }

    void waitForUsbButton()  // No idea how your code works for this, so here's a simulation.
    {
        Thread.Sleep(30000); // Simulate it taking 30s for the USB button to be pressed.
    }

    readonly CancellationToken _cancellation;
}

Then you can use a cancellation token to cancel waiting for the USB button press.

As an example of how to use this in a form, here I have created a form with a single button called button1 (the default when you add a new button to a form).

When you press this button, the code will start waiting for a USB button press, or for the user to press the A key on the keyboard. You can test this code by creating a default Windows Forms app and dropping a default button onto the main form (which will be called Form1 by default).

Then add the following code to Form1:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
        base.OnKeyDown(e);

        if (_cancellation == null)
            return;

        if (e.KeyCode == Keys.A)
            _cancellation.Cancel();
    }

    async void button1_Click(object sender, EventArgs e)
    {
        if (_cancellation != null) // Already waiting?
            return;

        button1.Enabled = false;
        button1.Text = "Waiting...";

        _cancellation = new CancellationTokenSource();
        UsbButtonWaiter waiter = new UsbButtonWaiter(_cancellation.Token);

        bool wasUsbButtonPressed = await waiter.WaitForUsbButtonAsync();

        if (wasUsbButtonPressed)
            MessageBox.Show("USB button was pressed");
        else
            MessageBox.Show("Cancel key was pressed");

        button1.Enabled = true;
        button1.Text = "button1";
    }

    CancellationTokenSource _cancellation;
}

If you run this application and click button1, its text will change to Waiting..., and it will become disabled.

Then nothing will happen until either you press A or 30s elapses, whereupon a message box will be displayed telling you if the USB button was pressed or the user cancelled, and the button will be re-enabled.

(Note that, of course, the USB button is never really pressed since it is just a simulation, but you can substitute the appropriate code.)

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276