3

I have an app with many user controls and many buttons on each, each button has an OnClick event which does some important stuff and then sends to a new user control.

The problem comes when the user clicks really fast multiple times, the event code gets executed more than once before exitting to a new user control, causing problems.

My current solution is to disable the button on the very first line of the event handler, but doing this to every window and handler would be troublesome, what can I do?

EDIT: Would it be a good solution to derive from Button, and override the OnClick event so it always does a check for a "working" variable, and if it is true, it doesnt start the event ? Something like:

public class MyButton : Button
{
private static bool isWorking = false;

protected override void OnClick(EventArgs e)
{
    if (!isWorking)
    {
        isWorking = true;
        base.OnClick(e);
        isWorking = false;
    }
    //Else do nothing
}

}

Cristiano Coelho
  • 1,675
  • 4
  • 27
  • 50

5 Answers5

3

You can use some timeStamp to delay between 2 clicks:

DateTime timeStamp;
//this will handle the clicks with the allowed interval being 0.5 second
//Note that a tick is equal to 1/10,000,000 of second.
private void click_Handler(object sender, EventArgs e) {
   if ((DateTime.Now - timeStamp).Ticks < 5000000) return;
   timeStamp = DateTime.Now;
   //your code goes here ....
}
King King
  • 61,710
  • 16
  • 105
  • 130
  • Same as above, i would have to replicate this code on every event handler – Cristiano Coelho Dec 21 '13 at 22:39
  • @user1777914 don't be such lazy, there is only 1 way you don't have to add the handler for some event for your control is to create your custom control, handle everything inside. I doubt that you don't even know **a handler can handle many events**, that means in this case the `click_Handler` can handle all the `Click` events of your buttons. You just need to check the `sender` to see which button is clicked, so **Why do you think there is any duplicated code** here? – King King Dec 22 '13 at 11:35
  • It would be great if i could add an Action Filter attribute (just like ASP.NET MVC ones) to my handlers, so i could execute code before and after. But C# does not support decorators nor interceptors =/ – Cristiano Coelho Dec 22 '13 at 18:09
1

If you want all buttons to wait until one button's work is done, add a bool isProcessing variable to your form. Wrap the work of each button inside an if (!isProcessing), and set that flag to true in the first line inside the if statement. Then don't forget to set it back to false right before you exit the if.

I'm assuming you're doing all of this asynchronously, since if it's all in the same thread, the form will lock while it's processing the work. This will solve your issue though.

krillgar
  • 12,596
  • 6
  • 50
  • 86
  • same as above, i would have to replicate the same code ( if !isProcessing, set true/false) on every event handler, i want to avoid this. – Cristiano Coelho Dec 21 '13 at 22:39
  • Unfortunately, there's not an easy solution to get all event handlers to do something without having to code them all. – krillgar Dec 22 '13 at 03:37
1

Disabling controls while sensitive operation is on-going is a typical solution that I always apply.

But since there can be quite a few controls on one screen that are affected by some click or change in UI, I typically design forms to have a specialized method which walks through all the affected controls and disables/enables them accordingly.

Something like this:

void EnableControls(bool enable)
{
    foreach (Control ctl in this.Controls)
        ctl.Enabled = enable;
}

Similarly, you could group controls into related buckets, so to disable/enable only one of them etc. Depends on your precise needs.

There is an alternative solution to use timer - disable the button, but enable it after 1 sec. This prevents nervous users from clicking multiple times if that would cause damage to data (i.e. each click is treated as a new operation).

Zoran Horvat
  • 10,924
  • 3
  • 31
  • 43
  • The problem here is that i would have to add this method (and its calls on each event handler) to every User Control in my form, im not talking about buttons, but user controls that changes and simulates navigation, i would like to do this only once in the whole code instead of copying the same code on every user control and event handler – Cristiano Coelho Dec 21 '13 at 22:37
  • The whole point is in not copying the code anywhere. If you can find the common logic, then you can set up the handlers once for all controls - just loop through all of them and subscribe to Click event of each button with the same handler. That handler calls enable/disable in the same way for all buttons on form. – Zoran Horvat Dec 21 '13 at 22:43
  • Theres something i think you didn't understand me, the problem is not having many buttons, but many user controls (each one simulates a different window) where each has 1 or 2 buttons which does sensitive work – Cristiano Coelho Dec 21 '13 at 22:56
1

I would call the same function from every button and then perform the specific task:

private void Button_Click(object sender, EventAgrs e)
{
    Button btn = sender;
    btn.disable = true;
    switch (btn.AccessibleName)
    // call specific function for the particular button or do it all here
}
Dean.DePue
  • 1,013
  • 1
  • 21
  • 45
0

I'm not sure if this would even work, but just an idea...

You could try with aspect oriented approach (with the help of Postsharp for example):

  1. Create two aspects, one for method entry and one for method exit. In the method entry mark the current method as 'processing' (add the method name to a hash set for example). In the method exit mark the method as 'not processing' (remove it from the hash set). Then in the method entry check if the method is processing and if it is, then cancel the method (like this: https://stackoverflow.com/a/2437794/113858)
  2. Mark all of your event handlers with this aspect.
Community
  • 1
  • 1
sventevit
  • 4,766
  • 10
  • 57
  • 89
  • Yes, using an aspect oriented approach would be good, i dont know why isnt this supported by C# natively, with ASP.NET MVC you have got action filters which can do pretty much what you described above, this should be possible without any external library. – Cristiano Coelho Dec 22 '13 at 22:53