This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to block Winforms UI while background thread is running
i am using a C# WinForm application
I have a Save button on the screen where the data of the screen is saved to database.
what happens, when user clicks to button application goes to database and saves data.
it takes some time.
but mean while if user again click on the Save button the Click event get catched and when the first Click return to main code (after saving database) the caught event get fired..
In short the click event get caught and fired when the thread returns from the first event
( I tried the scenario of enable/disable the button).
How can I stop this behavior.
Regards,
Akhil
#Jalal: I tried this code with some modification as
private readonly object _userActivityLocker = new object();
private void btnSave_Click(object sender, EventArgs e)
{
if (System.Threading.Monitor.TryEnter(_userActivityLocker))
{
//note that any sub clicks will be ignored while we are here
try
{
DateTime dt = DateTime.Now;
Thread.Sleep(2000);
Debug.Print("FirstClick {0} Second Click {1}",dt.ToLongTimeString(), DateTime.Now.ToLongTimeString());
//here it is safe to call the save and you can disable the btn
Application.DoEvents();
}
finally
{
System.Threading.Monitor.Exit(_userActivityLocker);
//re-enable the btn if you disable it.
}
}
}
but when i rapidly click on button (i checked with 5 times rapid clicks) 5 click events
has been fired and console window is showing
FirstClick 1:30:22 PM Second Click 1:30:24 PM
FirstClick 1:30:24 PM Second Click 1:30:26 PM
FirstClick 1:30:26 PM Second Click 1:30:28 PM
FirstClick 1:30:28 PM Second Click 1:30:30 PM
FirstClick 1:30:30 PM Second Click 1:30:32 PM
The problem is that your program is dead to the world while it is saving the data to the database. The user's mouse click is sitting in the message queue, waiting for your UI thread to come back to life. When it does, the button is no longer disabled so the Click event fires.
You can solve it by emptying the message queue before you re-enable the button so the click is processed while the button is disabled:
private void button1_Click(object sender, EventArgs e) {
button1.Enabled = false;
// Save data to database
//...
System.Threading.Thread.Sleep(2000);
Application.DoEvents(); // Empty the message queue
if (!button1.IsDisposed) button1.Enabled = true;
}
Do not skip the IsDisposed test, DoEvents is dangerous because it isn't selective about what events get processed. It will happily let the user close the main window while your code is still running.
But the better solution is to not let your UI thread go dead like this. Use a BackgroundWorker to perform the save on a worker thread. This will also avoid the ugly "Not Responding" ghost window that Windows puts up when your save takes more than a couple of seconds. It probably doesn't do this yet right now, but it will a year from now when the dbase has grown. You can re-enable the button in the BGW's RunWorkerCompleted event handler.
By enabling, then re-enabling again as you eluded to. What was the problem with this?
public void SaveButton_Click(..., ...)
{
this.SaveButton.Enabled = false;
Save();
this.SaveButton.Enabled = true;
}
using a System.Threading.Monitor class will do the trick like:
private readonly object _userActivityLocker = new object();
private void btnSave_Click(object sender, EventArgs e)
{
new Thread(delegate()
{
if (System.Threading.Monitor.TryEnter(_userActivityLocker))
{
//note that any sub clicks will be ignored while we are here
try
{
//here it is safe to call the save and you can disable the btn
}
finally
{
System.Threading.Monitor.Exit(_userActivityLocker);
//re-enable the btn if you disable it.
}
}
}) { IsBackground = true }.Start();
}
To prove that changing the button to enable or disable state is not enough here a simple test:
Add a new form and add a button1 to it and inside the button1 click event handler write the following code:
private void button1_Click(object sender, EventArgs e)
{
button1.Enabled = false;
Console.WriteLine("First Message");
Thread.Sleep(2000);
Console.WriteLine("second Message");
button1.Enabled = true;
}
and then build and run the application, double click on the button1 and the result int the output window will be:
First Message
second Message
First Message
second Message
so we have to make sure only one click is executed even when double click or so and that accomplished simply by using the System.Threading.Monitor
Update: Note that you can use a Task "if C# 4.0", a ThreadPool.QueueUserWorkItem or BackgroundWorker as a substitute of Thread.
Related
i would like to know how can i get focus on my loading form when an event is fired. As soon as when i press the button my loading screen pops up and after couple of seconds it loosing focus.. which mean, i am able to click my main form, especially all my columns cells and stuffs How can i get rid of this??
Codes:
private void button3_Click(object sender, EventArgs e)
{
bool done = false;
//thread pool should be used under a seperate manager so that UI dont get affected
ThreadPool.QueueUserWorkItem((x) =>
{
using (var splashForm = new Form4())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
move(); // move files
Datagrid_bind();
done = true;
}
If I understand your question correctly, you want the user not to be able to click on your main form while it's loading the data. If that's the case, you can simply hide your form while it loads:
myMainForm.Hide();
When it finishes loading, you call Show again:
myMainForm.Show();
I have an application where I need to display a loading form on some actions. So, I have a button that opens a form, and when I click on this button I need to display my loading form. I use the following code:
private void diligênciasToolStripMenuItem1_Click(object sender, EventArgs e)
{
LoadingWindow loadingWindow = new LoadingWindow();
try
{
loadingWindow.Show();
Cursor.Current = Classe_Cursor.LoadCustomCursor(#"D:\Wait (1).ani");
FormConsultaDiligencia Childform = new FormConsultaDiligencia();
Childform.MdiParent = this;
Childform.Show();
Cursor.Current = Cursors.Default;
loadingWindow.Close();
}
catch (Exception ex)
{
MessageBox.Show("" + ex.Message);
}
}
When I click this button the LoadingWindow opens, but the items that I have inside the form don't work:
All appears like blank, and it would appear like:
What is happening here? I can't get there alone, can someone explain to me what is the problem?
This occurs because you show form and execute prolonged operations into GUI thread. You should execute this operations in backgroung thread.
EDIT: code removed.
After the istruction loadingWindow.Show() the click function is hang by loadingWindow that seems no filled with components.
The code restart from Cursor.Current = Classe_Cursor.LoadCustomCursor(#"D:\Wait (1).ani") after the user close the loadingWindow.
The istruction loadingWindow.Close() is an error: at that point the loadingWindow is already closed.
I guess you have to load animation and strings in constructor of loadingWindow.
Anyway I think you need to use a thread to perform the creation of Childform. At the end that thread will send a close request to loadingWindow and start to show Childform.
Solution proposed by adv12 will be your gol easely.
I have this really little problem, but which can't be easily solved. Currently, my program has 2 buttons, a "Start" and a "Cancel". When the user clicks the start, the buttons should go instantly:
StartButton.IsEnabled = false;
CancelButton.IsEnabled = true;
But this occurs only when the BackgroundWorker has finished (all the code which will be ran after pressing the button), because the UI is always updated as last. There's no way I could add these commands to the "ProgressChanged" or "Completed" event of the backgroundworker. These events can take up to 10min to complete.
One easy way is to add these commands to the "ProgressChanged" part, and in the end "Complete" change their state again. But I'd like to avoid this, as the buttons should be showing their real state all the time, not after few "ProgressChanged" events. Of course there's always ways around, like not using the button's UI properties.
Is there any short solution for this?
It doesn't work to add the Button.Property changes to the ClickEvent. That's the main problem in this. I can easily use the "Completed" part of BGW to change the Button's back to match the starting state. The problem is to get them set right before all the events and BGW.
if you have a start button like:
this.StartButton = new System.Windows.Forms.Button();
then you can do
this.StartButton.Click += new System.EventHandler(this.button1_Click);
and then do
private void button1_Click(object sender, EventArgs e)
{
StartButton.IsEnabled = false;
CancelButton.IsEnabled = true;
Thread bg = new Thread(new ThreadStart( UpdateDatabase()));
bg.Start();
}
if you want the bg thread to send messages to the UI use the Invoke method like here
public delegate void UpdateUIHndler();
public void UpdateUI()
{
}
and do
if (InvokeRequired)
{
Invoke(new UpdateUIHndler(UpdateUI));
}
Take a look at a previous question of mine (quite similer). I should go for option 1.
ASP.NET Application log while code is running / progress bar
UI will only be delayed 5 seconds. Instead of text update the button styling using AJAX.
you can disable the start button in the click event of that button itself and enable it again it on RunWorkerCompleted event of BGW as shown below
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
private void StartButton_Click(object sender, RoutedEventArgs e)
{
startButton.IsEnabled = false;
}
void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
stratButton.IsEnabled = true;
}
void _worker_DoWork(object sender, DoWorkEventArgs e)
{
//Your processing code
}
I have 2 buttons the first one it's name "Continuous" .. the second one "Stop"
I want to call a method when press the continuous button :
private void continuous_Click(object sender ,EvantArgs e)
{ // how to get stop event
while(!StopEvent) method();
}
my question is : how can I stop the execution by pressing the stop button ??
I have a windows app which is just a form with a timer control on. I've managed to track this down to the following situation:
private void timer1_Tick(object sender, EventArgs e)
{
MessageBox.Show("Test");
timer1.Enabled = false;
}
Will print Test again and again until I stop the program. However:
private void timer1_Tick(object sender, EventArgs e)
{
//MessageBox.Show("Test");
textBox1.Text += "t";
timer1.Enabled = false;
}
Just adds a single "t" to the textbox.
Can anyone tell me why MessageBox.Show is causing the function to return before the timer is disabled?
The call to MessageBox.Show blocks execution of timer1_Tick until you close the messsagebox, so the call to set timer1.Enabled = false; doesn't occur until after that. Because of this, the timer is still running and thus timer_Tick` still keeps getting called, every time the timer fires, until you hit OK on one of the message boxes.
What you need, if you want displaying the messagebox to stop the timer from firing again, is:
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
MessageBox.Show("Test");
}
You disable the timer after the user clicked the messagebox away.
MessageBox.Show shows a modal dialog. It will return (to the caller method) after the user responded to the messagebox. If you disable the timer first, the event will not be triggered again, and the user will have enough time to react.
Try this:
timer1.Enabled = false;
MessageBox.Show("Test");
Are you clicking OK on test, each timer click? If the message boxes keep stacking up one on top of the other, it's because MessageBox.Show doesn't return until you close the messagebox. In the meantime a message pump will continue to run, and process your timer messages.