Do stuff while button pressed down? [solved]
-
Hello CPers, I'm trying to get a Winform to do some stuff while a button is pressed down, and stop doing it when the button's let go. I'm trying this out in a simple form with a button and a label. So as long as the button is pressed (MouseDown) the value displayed on the label should keep incrementing by 1. I've tried to used a BackgroundWorker for this and have so far failed. So, in my form class I have:
BackgroundWorker bwAsync = new BackgroundWorker();
public Form1() {
InitializeComponent();bwAsync.WorkerReportsProgress = true; bwAsync.WorkerSupportsCancellation = true; bwAsync.DoWork += new DoWorkEventHandler(bwAsync\_DoWork);
}
private void button1_MouseDown(object sender, MouseEventArgs e) {
bwAsync.RunWorkerAsync();
}
private void button1_MouseUp(object sender, MouseEventArgs e) {
bwAsync.CancelAsync();
}
void bwAsync_DoWork(object sender, DoWorkEventArgs e) {
BackgroundWorker bwAsync = sender as BackgroundWorker; int i; label1.Invoke( (MethodInvoker)delegate() { i = int.TryParse(label1.Text, out i) ? i : 0; while(!bwAsync.CancellationPending){ Console.WriteLine("down"); label1.Text = i.ToString(); i++; } });
}
What happens here is, once it goes into DoWork() it gets stuck in the loop, and it never gets to the MouseUp handler. Anyone have any feeback on my code, or on how it's supposed to be done? Would be much appreciated. Thank you.
modified on Sunday, June 13, 2010 11:47 PM
-
Hello CPers, I'm trying to get a Winform to do some stuff while a button is pressed down, and stop doing it when the button's let go. I'm trying this out in a simple form with a button and a label. So as long as the button is pressed (MouseDown) the value displayed on the label should keep incrementing by 1. I've tried to used a BackgroundWorker for this and have so far failed. So, in my form class I have:
BackgroundWorker bwAsync = new BackgroundWorker();
public Form1() {
InitializeComponent();bwAsync.WorkerReportsProgress = true; bwAsync.WorkerSupportsCancellation = true; bwAsync.DoWork += new DoWorkEventHandler(bwAsync\_DoWork);
}
private void button1_MouseDown(object sender, MouseEventArgs e) {
bwAsync.RunWorkerAsync();
}
private void button1_MouseUp(object sender, MouseEventArgs e) {
bwAsync.CancelAsync();
}
void bwAsync_DoWork(object sender, DoWorkEventArgs e) {
BackgroundWorker bwAsync = sender as BackgroundWorker; int i; label1.Invoke( (MethodInvoker)delegate() { i = int.TryParse(label1.Text, out i) ? i : 0; while(!bwAsync.CancellationPending){ Console.WriteLine("down"); label1.Text = i.ToString(); i++; } });
}
What happens here is, once it goes into DoWork() it gets stuck in the loop, and it never gets to the MouseUp handler. Anyone have any feeback on my code, or on how it's supposed to be done? Would be much appreciated. Thank you.
modified on Sunday, June 13, 2010 11:47 PM
Hi, your
label1.Invoke
causes the entire while loop to execute on the main thread (so it is allowed to touch the Label), however that also blocks the thread, so yourbutton1_MouseUp
handler cannot react to the button being released. (The main thread processes its event queue, popping and executing one message at a time). Furthermore, your while loop does not contain any timing control (such as a Thread.Sleep), so it will spin as fast as it can (depending on the characteristics and the load of your system), and clogging your Console. I would use a very different approach, without BGW: - have a class member of type System.Windows.Forms.Timer; - have a class member "int tickCount"; - inbutton1_MouseDown
clear tickCount and launch that timer, with an appropriate interval (say 100 msec), and a tick handler basically containing:Console.WriteLine("down");
tickCount++;
label1.Text=tickCount.ToString();- in
button1_MouseUp
simply stop the timer. The advantages would be: code is much simpler, everything runs on the main thread, which is idle most of the time, as all it does is run one tick handler every 100 msec. :)Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles]
Nil Volentibus Arduum
-
Hi, your
label1.Invoke
causes the entire while loop to execute on the main thread (so it is allowed to touch the Label), however that also blocks the thread, so yourbutton1_MouseUp
handler cannot react to the button being released. (The main thread processes its event queue, popping and executing one message at a time). Furthermore, your while loop does not contain any timing control (such as a Thread.Sleep), so it will spin as fast as it can (depending on the characteristics and the load of your system), and clogging your Console. I would use a very different approach, without BGW: - have a class member of type System.Windows.Forms.Timer; - have a class member "int tickCount"; - inbutton1_MouseDown
clear tickCount and launch that timer, with an appropriate interval (say 100 msec), and a tick handler basically containing:Console.WriteLine("down");
tickCount++;
label1.Text=tickCount.ToString();- in
button1_MouseUp
simply stop the timer. The advantages would be: code is much simpler, everything runs on the main thread, which is idle most of the time, as all it does is run one tick handler every 100 msec. :)Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles]
Nil Volentibus Arduum
-
THAT was ridiculously easy and clean. To think I spent the better part of the day trying to manipulate threads.. :doh: Thanks, Luc!
No problem. Yes one tends to find convoluted ways first. BTW: You may want to switch to a BGW as soon as some real work needs to be done. Then the calculations would go into the DoWork handler (without Invoke!) and hence really run on the BGW thread, however reporting progress or intermediate results to the GUI would best be handled by calling ReportProgress(), which causes the ProgressChanged handler to run on the main thread automatically. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles]
Nil Volentibus Arduum
-
No problem. Yes one tends to find convoluted ways first. BTW: You may want to switch to a BGW as soon as some real work needs to be done. Then the calculations would go into the DoWork handler (without Invoke!) and hence really run on the BGW thread, however reporting progress or intermediate results to the GUI would best be handled by calling ReportProgress(), which causes the ProgressChanged handler to run on the main thread automatically. :)
Luc Pattyn [Forum Guidelines] [Why QA sucks] [My Articles]
Nil Volentibus Arduum