MessageBox.Show() freezes execution : windows form c# - c#

Good day. I have a form and one backgroundworker . In the bw_Dowork event, there are instances when I need to print a message using MessageBox.Show()(i.e YES?NO box). However, whenever I call the messageBox.Show() method, the execution freezes and the form does not allow me to click my selection (i.e either Yes/No). Sometimes, if I want to work, I have to click fast as the message shows. Otherwise it freezes when I give a seconds of gap. Example of an instance where I use MessageBox.Show() is as shown below:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
if (fileFTP.Exists == false)
{
_busy.WaitOne();
if ((worker.CancellationPending == true))
{
e.Cancel = true;
return;
}
SetText("File Ftp.exe are missing. This file are required to preform update, please contact yout system administrator to retrive Ftp.exe", true);
MessageBox.Show("File Ftp.exe are missing. This file are required to preform update, please contact yout system administrator to retrive Ftp.exe");
goto ExitProgram;
}
}
After I did some research about this online, some suggested the MessageBox is interfering with the interface thread. This makes me trigger the Messages using delegates but all to no avail. I had to remove all the MessageBoxes. Leaving one still freezes my execution when fired. Please any help would be appreciated.

Try to use the main UI thread to show the messagebox:
this.BeginInvoke((Action)(() => MessageBox.Show("Hello")));
(assuming this is a Form)
Btw: "goto"? seriously?

Related

How to fix while loop crashing program

I'm trying to have serial data sent out continuously while a check button is being pressed in Visual Studio. So far, this is what I've come up with:
private void checkBox1_CheckedChanged_1(object sender, EventArgs e)
{
while (checkBox1.Checked)
{
serialPort1.Write("D");
Task.Delay(100);
if (checkBox1.Checked != true)
{
break;
}
} `
For some reason, whenever I launch the program and check the box, the program
freezes. I can't exit the program or minimize it or anything. It just stops in its tracks.
The user interface in Windows is based on messages. Your while loop runs in the thread where those messages would be handled. While your code runs, no messages are handled. The user interface cannot be redrawn and cannot react to user input.
Better approach: Use a Timer component. The Tick event of the Timer will be executed every n milliseconds according to the Interval property of the Timer. In the Tick event, you can check the Checkbox state and then do something or not depending on whether the checkbox is checked.
This is a good approach if "do something" is a very short activity. For longer activities, you nee a bit more complex setup that is beyond the scope of a SO question unless you provide more details.
You're blocking the UI thread because your event is being handled synchronously. Refer to this answer for some tips on async/await programming: How do I update the GUI from another thread?

Show a Window while application is closing

I've a very heavy application that takes some time to close itself (it also depends on the pc where it is running.. but this is not the matter right now)
I would like to show a custom window with a message during this closing time, but as soon as i call the "Shutdown" method every window disappears (except made for the MessageBox).
This is the code i'm trying to use to achieve my objective
void MainWindow_Closing(object sender, CancelEventArgs e)
{
Task.Factory.StartNew(() =>
{
var closingWaitTest = "application closing, please wait;
Application.Current.Dispatcher.Invoke(() =>
{
var closingSplash = new ClosingSplashWindow(closingWaitTest);
closingSplash.Show();
});
MessageBox.Show(closingWaitTest);
});
Application.Current.Shutdown();
}
I Added a messageBox just to check, and it actually works. I mean, the MessageBox stays open until the application process is alive (i check that from the windows TaskManager) while my Window is instantly closed.
Hope someone can give some advice about this,
thanks in advance (:
EDIT -
So, the main problem is that as soon as i call the Application.Current.Shutdown my splash window instantly closes, while the application process is still up and running for some time (disposing all my things before calling shutdown actually reduced this time a bit).
The point is that i would like to show a window for the entirety of time that the process is still up; given the fact that a MessageBox behaves exactly like that, my question is:
Is there a way to make my ClosingSplashWindow behave like a MessageBox and stay visible until the application process is really dead?
Since Application.Current.Shutdown(); is going to close the application immediately. Maybe you first have a flag to track that application is being closed, cancel the Closing event and initiate the Resource cleanup followed by Application.Current.Shutdown(); again.
The Application_Closing handler may get fired once again, since you've a flag which says you're about to close you can directly exit the handler and all should be good.
First of all, you want to have a flag which indicates that your application is currently shutting down:
private bool IsShuttingDown { get; set; }
Then you should cancel closing operation, perform some heavy work and shut down your application:
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (!IsShuttingDown)
{
e.Cancel = true;
ShowSplashWindow();
PerformHeavyOperation();
Application.Current.Shutdown();
IsShuttingDown = true;
}
}
In the end I solved the problem creating another application that shows a window while the main application process is still up and running.
I know it is not the best way to do it, but i was not able to do it in any other way..
Thank for the support! :)

How do I prevent any button click events from queuing until the event handle is finished with the first call

I want to prevent a button click from queuing. In testing I have a Form, a Button and in the Code-Behind I have the event handler:
private void button1_Click(object sender, EventArgs e)
{
if (_codeRunning)
return;
_codeRunning = true;
//Application.DoEvents();
//button1.Enabled = false;
_click ++;
Debug.WriteLine("Click Number: " + _click);
Task.Delay(5000).Wait();
//button1.Enabled = true;
_codeRunning = false;
}
When I run debug and click the button twice or three or four times rapidly, Debug Output shows each click about five seconds after the last one. What I would like it to show is a single Click and drop the rest until first Event is complete.
I have also tried to disable the button, as well as temporarily remove the Handler from the Button_click event. It is all the same results.
There are various amounts of trouble you'll get into when you hang-up the UI thread like this. This is certainly one of them, nothing pleasant happens when the user wildly bangs on the button to try to get something noticeable to happen. And sure, those clicks won't get lost, they stay stored in the message queue. To activate your Click event handler again when your event handler stops running.
Pretty important to learn how to use the BackgroundWorker or Task classes to avoid this kind of trouble. Just setting the button's Enabled property is then enough to solve this problem.
Purging the mouse clicks from the message queue is technically possible. But ugly to do, it requires pinvoke. I'll hesitantly post the alternative, don't assume that this is in general a good strategy. You'll need to read this post to have some insight into why DoEvents() is a dangerous method.
private void button1_Click(object sender, EventArgs e) {
button1.Enabled = false;
button1.Update();
'' long running code
''...
Application.DoEvents();
if (!button1.IsDisposed) button1.Enabled = true;
}
The Update() call ensures that the user gets the feedback he needs to know that banging the button repeatedly isn't going to do anything useful. The DoEvents() call will dispatch all the queued-up mouse clicks, nothing happens with them since the button is still disabled. The IsDisposed test is essential to solve the problem with DoEvents(), it ensures your program won't crash when the user clicked the window's Close button while the code was running.
Use the HourGlass class in this post to provide more feedback.
I had a button that on click event was going to run a method. Same issue happent and when the user clicked multiple times the method was triggered multiple times. So I made a boolean and changed it value when the method started.
private bool IsTaskRunning = false;
private void MyMethod()
{
if ( IsTaskRunning==false )
{
IsTaskRunning=true;
// My heavy duty code that takes a long time
IsTaskRunning=false; // When method is finished
}
}
So now the method runs only if it's done the last time.

I can only close a form once, InvalidOperation Exception Invoke or BeginInvoke cannot be called on a control until the window handle has been created

Hi
I'm opening a form like this from my main form when the user makes a selection of a menu item.
private void commToolStripMenuItem_Click(object sender, EventArgs e)
{
Command_Form Command_Form1 = new Command_Form();
Command_Form1.ShowDialog();
// Command_Form1.Dispose(); this didn't help
}
Inside the form "Command_Form1"
I close it like this when the user clicks on the close button
private void Close_button_Click(object sender, EventArgs e)
{
this.Close(); //I get the exception here
}
This process works fine once but on the second closing of the form
(Which I hope is a completely different/new instance of the form) I get the error in the title of this post.
This is the output in the debug window.
A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll
All the topics that list this error go on about not trying to do anything to a form that has not been displayed but this happens when I click on a button in the form.
It would seem to me that pretty much ensures the form has been displayed if I'm able to click its button.
The other posts I've found that list this type of error go on about making thread safe calls so I tried this as an experiment but it didn't make any difference.
private void Close_button_Click(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
CloseCallback d = new CloseCallback(Close_button_Click);
Invoke(d, new object[] { sender, e });
}
else
{
this.Close();
I have multiple threads in my application but they are created by the controls I'm using not by me explicitly.
I am passing data from a serial port to/from the form by Marshling the received/sent data via a delegate[s].
It makes sense that the serial port would run on a different thread than the form but why would a button click on a form be in a diffrent thread than the form????
The whole thread thing is very confuzing
How do I figure out what threads originated where and what is going on in the threads that I didn't create explicitly?
Why would I need to invoke the form's close method via a delegate?
Heck is there anything I can do in this multi threading environment that is thread safe How in do I know if what I'm doing is unsafe/safe if I don't know what/where/why/who/when is creating threads?
My guess is your close() call is not throwing that exception, but something that happens after close(). Have you stepped into the code with the debugger to see when it is fired?
As to when you need to invoke...There is only one thread allowed to make changes and access dynamic properties on the GUI, call it the GUI thread. The GUI thread is responsible for updating layout, firing events like buttons, etc. If you ever access the GUI from another thread (like a timer event) you need to use invoke() to queue your function to be run on the GUI thread. BeginInvoke will also queue the function but is asynchronous (will only queue the function to be run on GUI thread, but will not wait for it to finish).
Close_button_click will only be called by your gui thread when the button click event fires(unless you explicitly call it somewhere else in your code behind, not recommended!), so invokeRequired=false in your code above, and the invoke code is never executed.

Why is Form.Refresh() not working?

I'm running a data import, using a Windows form to kick off the import and show progress.
I've got this whole thing so nice and user friendly, with major and minor progress bars and everything... but just one problem... the form refresh keeps going AWOL.
I have a call to Form.Refresh() every time I update my labels/progress bars, and it usually starts off working. But if ever I need to break into debug mode, just to hand-hold the import a bit, the Refresh() call stops working, and sometimes even if I'm running without Debug mode, at some unpredictable point the same thing happens: the labels and progress bars do not get updated, and if you hide the form and reopen it, the form does not repaint at all - it just shows up entirely white.
Why, oh why, does Form.Refresh() stop working, and how can I fix this?
It sounds as if the import runs on the UI thread, which means that this thread is blocked, preventing the form from repainting itself. A better approach would be to use a BackgroundWorker component, do the import in the DoWork event handler and use the ProgressChanged to update the UI.
Example:
private void StartImport()
{
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.RunWorkerAsync();
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// do some work simulating a lenghy process which occasionally
// reports progress with data back to the caller
for (int i = 0; i < 100; i++)
{
Thread.Sleep(200);
backgroundWorker.ReportProgress(i, "Item No " + i.ToString());
}
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listBox.Items.Add(e.UserState.ToString());
}
Using this approach you will typically not need to call Refresh to force a repaint of the form.
You may want to change your code into using BeginUpdate and EndUpdate, like so:
Control.BeginUpdate();
// Do something to the control, e.g. add items or whatnot
Control.EndUpdate();
This way Refresh shouldn't be necessary.
AFAIK constantly calling Refresh is really a hack and should be avoiding, as it stresses the CPU quite a bit (it has to refresh everything instead of just the things which are changed).
Edit: If the form starts being white, it seems the drawing code is not been called at all, which indicates it's somewhat not responding.
I'd check the code for anything that can deadlock or otherwisely hang.
You could use observer pattern..in short if anything changes in model observer pattern will make sure that change is visible on form..
google it for some examples..
Depending on what .NET framework you're using, you can use the Task.Run approach:
private void btnShowProgress_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
Task.Run(() =>
{
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(100);
progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value = i; }));
}
});
}
Task.Run info
Using invoke with controls
The solution may not be the best practice but it definitely works for small applications.
In Form1 create a bool to check to see if the form is closed.
public bool formclosed = false
Then in Form2 on the Form Closing Event Handler add
formclosed = true
also in the Form2 after
InitializeComponent();
add
formclosed = false;
In Form1 create a timer.
In the timer1.Tick event handler say
private void timer1_Tick(object sender, EventArgs e)
{
if(formclosed == true)
{
Application.Restart();
}
}
This will restart the application and refresh everything ... I also had my text saved to the Properties.Settings.Default so everytime the application started the default settings would show.
I created an initial version of a Progress control using a BackgroundWorker. The Progress control computed and displayed nice things like Estimated Duration, Estimated Time to Completion. Each statistic was displayed by custom control based on a Label control. The Progress control worked in my production code.
Then I made some changes to the Progress control. I switched from basing my custom statistics controls from Label to Panel. I successfully ran all my tests (using NUnit). Then, I created a Test Windows Forms app. The Progress control successfully worked in the Test Windows Forms app.
When I ran my production Windows app with the updated Progress control it didn't display the statistics.
I tried Thread.Sleep(N), Form.Refresh(). None of those worked to update the statistics.
I eventually called Control.Invalidate() on each Statistic control. The Invalidate caused OnPaint to be called and the control happily updated the display with its specific statistic.
For Each Stat_Obj As Control in Statistics_Controls
Stat_Obj.Invalidate()
Next
You might need to give the window time to redraw itself. I understand you're doing the import in a loop and the loop is running on the main UI thread? Try adding this line to the loop:
Application.DoEvents();

Categories

Resources