button click event
-
lets say i have a simple form with a button on it. let button1_Click() be the function binded by the eventhandler delegate. the problem: i need this function to do a certain task that may take some time. i don't want this to freeze the UI(user interface) however i need to stop any further calls to button1_Click() for the duration of the task. sounds realy easy but to my surprise it's not that easy. take the following examples of the button1_Click function bodys:
{ this.button1.Enabled=false; //do task this.button1.Enabled=true; }
visualy disables the button but if you push it (even if disabled) X times the function will execute exactely X times.{ if(this.ignore) return; this.ignore=true; //do task this.ignore=false; }
same as above just that it doesn't visualy disable the button. ideas, solutions and explanations apreciated. -
lets say i have a simple form with a button on it. let button1_Click() be the function binded by the eventhandler delegate. the problem: i need this function to do a certain task that may take some time. i don't want this to freeze the UI(user interface) however i need to stop any further calls to button1_Click() for the duration of the task. sounds realy easy but to my surprise it's not that easy. take the following examples of the button1_Click function bodys:
{ this.button1.Enabled=false; //do task this.button1.Enabled=true; }
visualy disables the button but if you push it (even if disabled) X times the function will execute exactely X times.{ if(this.ignore) return; this.ignore=true; //do task this.ignore=false; }
same as above just that it doesn't visualy disable the button. ideas, solutions and explanations apreciated.I think you should consider porting the task you wish to do on a separate thread. This will not "freeze" your GUI since it will be running on a separate thread, other than the main (GUI) one. By doing this, you could also specify that you wish to wait for your thread-processing to complete before executing the rest of your code. If you haven't implemented multiple threads before, then may I suggest to Google it. A simple google search for something like "C# Thread tutorial" or "C# Multithreading" will bring up tons of results. Hope this helps Regards, Polis Can you practice what you teach?
-
I think you should consider porting the task you wish to do on a separate thread. This will not "freeze" your GUI since it will be running on a separate thread, other than the main (GUI) one. By doing this, you could also specify that you wish to wait for your thread-processing to complete before executing the rest of your code. If you haven't implemented multiple threads before, then may I suggest to Google it. A simple google search for something like "C# Thread tutorial" or "C# Multithreading" will bring up tons of results. Hope this helps Regards, Polis Can you practice what you teach?
the problem is not the freeze of the GUI. that i can simply avoid by multithreading as you pointed out. the issue is stopping any further calls of the function during the execution of the task. i have run into this problem when i had a task that needs like about 500 miliseconds to run (more exactely it's a MoveNext). i decided not to multythread it since it's quite short. the problem is when a users starts pounding the NextButton with 20 clicks a second (it happend to my by accident in a test) the application starts to slow down since it's ordered to do like 20 tasks/second and it can only do 2/second. not to mention sometimes it has to acces sensible data that has to be locked down for thread safety purposes and it sometimes takes like 2-5 minutes for it to actualy recover. there are more issues here like race conditions and so forth. now i thought that blocking any calls to the function would be the easyest way to do it. however it turned out not to be.
-
the problem is not the freeze of the GUI. that i can simply avoid by multithreading as you pointed out. the issue is stopping any further calls of the function during the execution of the task. i have run into this problem when i had a task that needs like about 500 miliseconds to run (more exactely it's a MoveNext). i decided not to multythread it since it's quite short. the problem is when a users starts pounding the NextButton with 20 clicks a second (it happend to my by accident in a test) the application starts to slow down since it's ordered to do like 20 tasks/second and it can only do 2/second. not to mention sometimes it has to acces sensible data that has to be locked down for thread safety purposes and it sometimes takes like 2-5 minutes for it to actualy recover. there are more issues here like race conditions and so forth. now i thought that blocking any calls to the function would be the easyest way to do it. however it turned out not to be.
Why don't you simply disable the button and enable it after the operation completes? Something like
private void buttonClick(object sender, EventArgs e)
{
thisButton.Enabled = false;
DoLongOperation();
thisButton.Enabled = true;
}If you can't enable/disable, you can have a flag to simulate it, set the flag to false before the operation and reset it after the operation. The function would then first check if the flag is true before proceeding. Regards Senthil _____________________________ My Blog | My Articles | WinMacro
-
Why don't you simply disable the button and enable it after the operation completes? Something like
private void buttonClick(object sender, EventArgs e)
{
thisButton.Enabled = false;
DoLongOperation();
thisButton.Enabled = true;
}If you can't enable/disable, you can have a flag to simulate it, set the flag to false before the operation and reset it after the operation. The function would then first check if the flag is true before proceeding. Regards Senthil _____________________________ My Blog | My Articles | WinMacro
obviously you haven't payed attention to my first post. the first example does just that. disabling the button and enabling it after the task is done. however it doesn't behave as expected. Visualy the button is disabled, but the function executes as many times as you push the button, regardless of it's state (disabled/enabled). if you put a counter in it and look at it after the test you'll see it's nice and neat the number of clicks you made. i think that this is because somehow it stacks the events and fires them after the function executes. try and run this code:
public void task() { Thread.Sleep(1000); this.counter++; } private void button1_Click(object sender, System.EventArgs e) { this.button1.Enabled=false; Thread t=new Thread(new ThreadStart(task)); t.Start(); while((t.ThreadState & (System.Threading.ThreadState.Stopped | System.Threading.ThreadState.Unstarted)) == 0) { Thread.Sleep(100); } t.Join(); this.button1.Enabled=true; Debug.WriteLine(this.counter); }
this has multi threading too. i can't exclude the possibility i'm doing something wrong but i can't seem to see where. pointers anyone? -
lets say i have a simple form with a button on it. let button1_Click() be the function binded by the eventhandler delegate. the problem: i need this function to do a certain task that may take some time. i don't want this to freeze the UI(user interface) however i need to stop any further calls to button1_Click() for the duration of the task. sounds realy easy but to my surprise it's not that easy. take the following examples of the button1_Click function bodys:
{ this.button1.Enabled=false; //do task this.button1.Enabled=true; }
visualy disables the button but if you push it (even if disabled) X times the function will execute exactely X times.{ if(this.ignore) return; this.ignore=true; //do task this.ignore=false; }
same as above just that it doesn't visualy disable the button. ideas, solutions and explanations apreciated.put this as a class variable
private ThreadStart ts= new ThreadStart(urfunctionhere); private Thread t=new Thread(ts);
then in ur button u could do{ button.enabled=false; if(t!=null&&!(t.isAlive)) //this will check if the user hits the button when ur function is runing -- if the thread its stopped will start-- { t.start(); } button.enabled=true; }
hope this help u -
put this as a class variable
private ThreadStart ts= new ThreadStart(urfunctionhere); private Thread t=new Thread(ts);
then in ur button u could do{ button.enabled=false; if(t!=null&&!(t.isAlive)) //this will check if the user hits the button when ur function is runing -- if the thread its stopped will start-- { t.start(); } button.enabled=true; }
hope this help uwell for one your example doesn't work. mainly because that's not the propper way to test if a thread is running. however even if i correct that problem it still doesn't solve my problem because now you have to sincronize the started thread. if you will read my third post you'll see a sincronised example that well you guessed it .. doesn't stop the calls.
-
lets say i have a simple form with a button on it. let button1_Click() be the function binded by the eventhandler delegate. the problem: i need this function to do a certain task that may take some time. i don't want this to freeze the UI(user interface) however i need to stop any further calls to button1_Click() for the duration of the task. sounds realy easy but to my surprise it's not that easy. take the following examples of the button1_Click function bodys:
{ this.button1.Enabled=false; //do task this.button1.Enabled=true; }
visualy disables the button but if you push it (even if disabled) X times the function will execute exactely X times.{ if(this.ignore) return; this.ignore=true; //do task this.ignore=false; }
same as above just that it doesn't visualy disable the button. ideas, solutions and explanations apreciated.You may try the following method if it works (it apparently works on my machine; however I cannot test in your conditions). As an event can be handled by more than one event handler, you may declare two event handlers for the same button click by using two different methods, as under:
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click += new System.EventHandler(this.button1_Click2);It is pertinent to mention here that when an event is handled by more than one handler, all the methods handling that event are executed. The order in which the methods execute is the same as the order in which the association was created. Thus, in this case, "button1_Click" method will be executed first and thereafter the second method "button1_Click2" will be executed. What I suggest it that you disable the button1 in the first such method and do your long task in the second method. As by the time the second method starts, the first method must already have been executed (meaning that the button1 is now already disabled), you can safely do your long task in the second method. The code can be something like this:
private void button1_Click(object sender, System.EventArgs e)
{
this.button1.Enabled = false;
}private void button1_Click2(object sender, System.EventArgs e)
{
//do the long task here//After finishing the long task, re-enable button1 this.button1.Enabled = true;
}
I am not sure, but I hope that your problem can be solved in this manner.
-
obviously you haven't payed attention to my first post. the first example does just that. disabling the button and enabling it after the task is done. however it doesn't behave as expected. Visualy the button is disabled, but the function executes as many times as you push the button, regardless of it's state (disabled/enabled). if you put a counter in it and look at it after the test you'll see it's nice and neat the number of clicks you made. i think that this is because somehow it stacks the events and fires them after the function executes. try and run this code:
public void task() { Thread.Sleep(1000); this.counter++; } private void button1_Click(object sender, System.EventArgs e) { this.button1.Enabled=false; Thread t=new Thread(new ThreadStart(task)); t.Start(); while((t.ThreadState & (System.Threading.ThreadState.Stopped | System.Threading.ThreadState.Unstarted)) == 0) { Thread.Sleep(100); } t.Join(); this.button1.Enabled=true; Debug.WriteLine(this.counter); }
this has multi threading too. i can't exclude the possibility i'm doing something wrong but i can't seem to see where. pointers anyone?Yeah, you're right. What's happening is after the button gets disabled, all the clicks keep getting added to the message queue and once your button gets enabled, they get dispatched to the button and so you keep getting the event as many times as the user clicked. To prevent that from happening, just call Application.DoEvents() before enabling the button. That'll clear off all those click messages in the queue and therefore you wont get the Click event. Just do
public void task()
{
Thread.Sleep(1000);
this.counter++;
}
private void button1_Click(object sender, System.EventArgs e)
{
this.button1.Enabled=false;
Thread t=new Thread(new ThreadStart(task));
t.Start();
while((t.ThreadState & (System.Threading.ThreadState.Stopped | System.Threading.ThreadState.Unstarted)) == 0)
{
Thread.Sleep(100);
}
t.Join();Application.DoEvents(); // This is the only change.
this.button1.Enabled=true;
Debug.WriteLine(this.counter);
}Regards Senthil _____________________________ My Blog | My Articles | WinMacro
-
Yeah, you're right. What's happening is after the button gets disabled, all the clicks keep getting added to the message queue and once your button gets enabled, they get dispatched to the button and so you keep getting the event as many times as the user clicked. To prevent that from happening, just call Application.DoEvents() before enabling the button. That'll clear off all those click messages in the queue and therefore you wont get the Click event. Just do
public void task()
{
Thread.Sleep(1000);
this.counter++;
}
private void button1_Click(object sender, System.EventArgs e)
{
this.button1.Enabled=false;
Thread t=new Thread(new ThreadStart(task));
t.Start();
while((t.ThreadState & (System.Threading.ThreadState.Stopped | System.Threading.ThreadState.Unstarted)) == 0)
{
Thread.Sleep(100);
}
t.Join();Application.DoEvents(); // This is the only change.
this.button1.Enabled=true;
Debug.WriteLine(this.counter);
}Regards Senthil _____________________________ My Blog | My Articles | WinMacro
-
Alternatively, you could just decouple the event handler and then recouple it after the call.
-
Alternatively, you could just decouple the event handler and then recouple it after the call.
yeah that crossed my mind too. but it seems a bit sadistic to me anyways. funny thing is i knew about Application.DoEvents() and actualy used it .. but to no result. in my test i placed it in the while loop because normaly you want from time to time the app to respond to other calls but oddly it didn't work(no idea why thow). the other thing that crossed my mind was to add a messageFilter before thread.Start and remove it after my task finished. but that's a bit too costly in my opinion.