Question

I'm working on a WinForms app and I have a user control in it. The buttons in the user control raise events up to the form to be handled by other code. One of the buttons starts some processses that will cause problems if they run simultaneously. I have logic in the code to manage the state so typically a user can't run the process if it's already running. However, if the user double-clicks the button it will start the process twice so quickly that it's tough for me to prevent it.

I'm wondering, what's the best way to handle this?

I started out by disabling the button in the click event but the second click comes in before the first click causes the button to be disabled. Setting other flags in the code didn't catch it either.

I'm considering adding some sort of sync lock on the code that raises the event but I'm wondering if any of you have a better idea.

Since this project is mostly complete I'm looking for answers that don't involve a radical rewrite of the app (like implementing the composite application block), however, feel free to post those ideas too since I can use them in my next projects.

Was it helpful?

Solution

Make sure that your button disabling or any other locking that you do is the /first/ thing that you do in your event handler. I would be extremely surprised if you could queue up two click events before even the first instruction fires, but I suppose that's possible if you're on a very slow computer that's bogged down with other apps.

In that event, use a flag.

private bool runningExclusiveProcess = false;

public void onClickHandler(object sender, EventArgs e)
{
    if (!runningExclusiveProcess)
    {
        runningExclusiveProcess = true;
        myButton.Enabled = false;

        // Do super secret stuff here

        // If your task is synchronous, then undo your flag here:
        runningExclusiveProcess = false;
        myButton.Enabled = true;
    }
}

// Otherwise, if your task is asynchronous with a callback, then undo your flag here:
public void taskCompletedCallback()
{
    runningExclusiveProcess = false;
    myButton.Enabled = true;
}

If you can still get in two clicks off of something like that, then make sure you're not accidentally subscribed to a doubleClick event, or anything else wonky is going on.

OTHER TIPS

Are you handling the event twice?

I am very surprised to read that the second click is coming in before you can disable the button. I would look to make sure you aren't hooking up the event twice. I have done this by accident before. You will then get two events, almost instantaneously.

The disable flag on that button would not be set until the button event handler completes, and any additional clicks will be queued in the windows message queue behind the first click. Make sure the button event handler completes quickly to free up the UI thread. There are several ways of doing this, but all involve eather spawning a thread or keeping a worker thread ready and waiting for an AutoResetEvent.

By any chance, are you labeling the button with an icon? By all means use code to prevent running two processes at once, but you can reduce double-clicking on the human side if your button looks and acts like a conventional command button (rectangular, text label only, "raised" surface that depresses on a click). Just a thought.

Disable the button as soon as it is clicked. So, the event can't be handled twice. As soon as the event had been handled, you can re-enable the button.

The checked answer is close, if anecdotal at best. To the uninitated it's missing too much code to make sense. I've written an article about using Timers and Threads and the code example is REALLY easy to understand. Try reading through it and seeing if you can set the flags from the example above by running your task process in a new Thread:

http://www.robault.com/category/Threading.aspx

Disable the button after the user first clicks it and before starting the task. When task completes, re-enable the button.

I started out by disabling the button in the click event but the second click comes in before the first click causes the button to be disabled. Setting other flags in the code didn't catch it either.

Given WinForms is inherently single-threaded, it should not be possible for the second click to fire before the processing for the first has completed (unless you are using new threads or BackgroundWorkers etc).

Can you show us a sample illustrating your problem?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top