0

The application is a machine control, so it needs access to ui to show status etc. (I know, goes against the recommendation to separate UI and work code, but it is what it is, at least for now). The issue boils down to this: When one button event handler is not finished, another button needs to be clicked twice. First click gives the focus to the button, next click fires the event.

Here is the issue simplified to extreme. There are two buttons and a label. Stop button needs two clicks to stop the machine:

bool Stop = true;

private void Start_button_Click(object sender, EventArgs e)
{
    RunMachine();
}

private void Stop_button_Click(object sender, EventArgs e)
{
    Stop = true;
}

private void RunMachine()
{
    Stop = false;
    Status_label.Text = "Running";
    do
    {
        Application.DoEvents();
        Thread.Sleep(50);
    } 
    while (!Stop);
    Status_label.Text = "Stopped";
}

How can I make the button to react to the first click?

JuKu
  • 3
  • 1

2 Answers2

0

DoEvents() is bad. Don't use it.

If you have to use it (e.g. as workaround), then you are adding technical debt and likely to pay in the future, similar to your case.

A better approach is to run work inside the task and use cancellation token, but in your case the minimum modification required is this (add async modifier to a method):

while (!Stop)
{
    await Task.Delay(50);

    // or

    await Task.Run(() => Thread.Sleep(50));
}

The UI should be responsive now.

The latter is simulating synchronous code, put it instead of Sleep, don't forget to invoke if there you have to modify UI.

Sinatr
  • 20,892
  • 15
  • 90
  • 319
0

Thank you! I wasn't aware of the implications of Doevents, and using async and await is just as simple. I added a counter to show myself that the toy example is doing what I think it is. To make the answer complete and to help other noobs like me that might search answers for the same issue, here is the full example again. This works as wanted (stops with one click) and doesn't leave the RunMachine() running if the main form is closed without clicking stop. (My real application has enough code in the form closing event to prevent that, but I certainly wasn't aware of the trap.)

    bool Stop = true;

    private async void Start_button_Click(object sender, EventArgs e)
    {
        await RunMachine();
    }

    private void Stop_button_Click(object sender, EventArgs e)
    {
        Stop = true;
    }

    internal async Task RunMachine()
    {
        Status_label.Text = "started";
        Stop = false;
        int i=0;
        do
        {
            await Task.Delay(500);
            Status_label.Text = i.ToString();
            i++;
        } while (!Stop);
        Status_label.Text = "Stopped";
    }
JuKu
  • 3
  • 1