views:

443

answers:

2

I've got a Windows Forms Button on a Form which submits a web request. I want to be able to disable the button when it is first clicked and then re-enable it when I get a response. I don't have much control over the code that is being called and how it is being called so all I can play around with are Button events or I can create my own button that inherits from Button like so:

public class SingleClickButton : Button
{
 protected override void OnClick(EventArgs e)
 {
  bool wasEnabled = this.Enabled;
  this.Enabled = false;

  if (wasEnabled)
  {
   base.OnClick(e);
  }
 }
}

I have to call the base OnClick method last as the button won't disable until the web request has completed.

The problem I am having is that if the user does click multiple times the click events seem to build up and are all still executed. Is there maybe a way to cancel all queued events? Or is there a far simpler solution to my problem?

A: 

I had a similar problem. Solved it by adding a timer to the form, then using that timer to reenable the button which I disable on the click event.

    private void btnPrintReport_Click(object sender, EventArgs e)
    {
        btnPrintReport.Enabled = false;
        timerEnablePrint.Enabled = true;
        BackgroundProcess.Add("Printing", new WaitCallback(generateReport), null);
    }


    private void timerEnablePrint_Tick(object sender, EventArgs e)
    {
        btnPrintReport.Enabled = true;
        timerEnablePrint.Enabled = false;
    }

Set the timer to a sensible interval. I use 4 seconds (it's just to avoid users doubleclicking)

sindre j
And what happens, when the operation take more than 4 seconds :)?
TcKs
+5  A: 

You need use this scenario:

public class SingleClickButton : Button
{
        protected override void OnClick(EventArgs e)
        {
                this.Enabled = false;
                RunAsynchronousMethod( CallBack );
                base.OnClick(e);
        }

        void CallBack()
        {
                this.Enabled = true;
        }
}

The "RunAsynchronousMethod" can create new "Thread" or use "ThreadPool.QueueUserWorkItem)".

EDIT:

public class SingleClickButton : Button {
        protected override void OnClick(EventArgs e) {
                this.Enabled = false;
                RunAsynchronousMethod( CallBack );
                base.OnClick(e);
        }

        void CallBack() {
                this.Enabled = true;
        }

        void RunAsynchronousMethod( Action callBack ) {
                // there you can use ThreadPool or Thread
                ThreadPool.QueueUserWorkItem( this.Worker, callBack );
        }

        void Worker( object callBack ) {
                try {
                    // some operations
                }
                finally {
                    // after operations was proceeded, the callback function will
                    // be called
                    ((Action)callBack)();
                }
        }
}
TcKs
Just to confirm, Callback is the method where all the work happens? If so calling this.Enabled inside it will cause a cross thread error.
Rob Bell
No, Callback function is called AFTER the work is done. Look at my edit, for details.
TcKs