I try to show a MESSAGE to the user while an operation is executed. The MESSAGE won't show any button. Just a MESSAGE (text) and maybe a background image.
The problem is the following:
MessageBox does not seem to be the good control (because of button and it blocks the running process).
Form.ShowDialog() also blocks the running process. I don't know what to do.
I want to show the message, run my process, and dispose the message when the process is done.
How to achieve this in C# ?
Create a simple form with the message (or expose a public property to be able to change the message, or a constructor with message parameter to pass it in) and show the form using this Show overload. Then disable the (entire) original (owner) form (or just disable the controls you don't want accesible).
So, in your "main" form do this:
Form f = new MessageForm();
f.Show(this); //Make sure we're the owner
this.Enabled = false; //Disable ourselves
//Do processing here
this.Enabled = true; //We're done, enable ourselves
f.Close(); //Dispose message form
Also, consider using a BackgroundWorker.
create custom form, and write own behavior
Create a custom form set it to minimal styles etc. Make sure it knows when the process is complete, say by passing in a reference to the process and checking it every now and then.
One way you could do it is create a worker class that raises an event when it is finished.
execute the worker class in a new thead so it runs it the backgroud.
create the modal form with a method to close it as the event handler for the "finished" event.
This way the ui still works and responds and the form will close once the worker thread is finished.
Related
It is possible that when I use:
MessageBox.Show("hello");
..to still use the form but the MessageBox is open like a second window?
because currently, when I show the MessageBox, I need to first click yes or no then I can use my form again.
Yes, you can!
If you simply want to display a MessageBox, don't care what happens with it, and don't want to wait until it's closed, you may launch it on a separate thread. The easiest way to do that would be using Task.Run().
Here's an example:
private void button1_Click(object sender, EventArgs e)
{
Task.Run(() => MessageBox.Show("hello"));
// The remaining code will run without waiting for the MessageBox to be closed.
}
A couple of notes worth mentioning:
Only use this for simple message boxes where you don't care about the result. If you want to act based on the result and execute something (on the main thread), things get a little bit trickier.
You won't be able to keep the MessageBox on top of the form. Once you interact with the form, it will come on top. If you need to keep the MessageBox on top and still have the ability to interact with the form, a custom MessageBox (i.e., form) would be better in that case because you can set the Owner property to keep it on top.
No. The application focus remains on the Message Box until it is dismissed. According to the documentation:
It is a modal window, blocking other actions in the application until the user closes it.
What you can do is create your own form, style it to look like a standard Message Box, and show that form using the .Show() functionality on an instance of it:
var messageBox = new MyCustomMessageBox("hello");
messageBox.Show();
If you want the functionality to be identical in usage, you could even add a static method to your custom form which encapsulates it:
public static void Show(string message)
{
new MyCustomMessageBox(message).Show();
}
Though what I wouldn't recommend doing is calling your custom form MessageBox, that would just be asking for confusion.
Closing a form from inside an Invoke like this:
Invoke(new Action(() => {
Close();
MessageBox.Show("closed in invoke from another thread");
new Form1();
}));
throws an exception as soon as the form is closed:
Invoke or BeginInvoke cannot be called on a control until the window
handle has been created.
But only on NET 4.0. On NET 4.5 no exceptions are thrown.
Is this expected behavior? How should I go about it?
That's because Close method closes the form and destroys it's handle and then the MessageBox is invoked in the Closed form with no handle, so the error message shows up.
I don't understand your purpose, but you should either move the code after Close out of invoke, or move the Close after them. For example:
Invoke(new Action(() => {
Hide();
MessageBox.Show("closed in invoke from another thread");
new Form1();
Close();
}));
Edit:
MSDN note about Control.Invoke:
The Invoke method searches up the control's parent chain until it finds a control or form that has a window handle if the current control's underlying window handle does not exist yet. If no appropriate handle can be found, the Invoke method will throw an exception. Exceptions that are raised during the call will be propagated back to the caller.
If you start a thread during initialisation, you do not know how far the initialisation has gone in another thread.
You notice differences in behavior on different .Net versions, but you cannot be sure about the order of things on different machines.
I have solved a lot of threading issues in Windows forms using my own messagepump, using a Queue and a normal Timer control:
Add a timer control to your form, with a small interval (250 ms)
Add a Queue to your form.
Let the timer event dequeue the actions, and execute it.
Add Actions to the queue during initialisation or even other background jobs.
Using this approach will issues with background jobs during initialisation, but also during closing/disposing of the form, since the timer will only trigger if the form is fully functional.
I'm trying to write a function that will dynamically create tab pages, populate the tabs, and then return data regarding what it has done. There are events that will tell me when each tab is done, so I'll be able to tell when they're all loaded.
All the work is UI-related, and my understanding is that UI stuff has to happen in the main thread, so it sounds like I can't just create a thread to do this and then wait on it in my function. Am I misunderstanding the issue with UI stuff having to be on the main thread? How can I have my function wait without blocking the work that has to happen in the UI?
EDIT: To clarify, the tab pages have webbrowser controls which will load various web pages, so those take some amount of time and I get a DocumentCompleted event for each when it's loaded. The idea is this: The caller hands me a list of urls. I pop up a form and create a tab page containing a web browser control for each url, and load one url in each tab. These will complete at different times, each firing a DocumentComplete event handler when a page has loaded. When they've all loaded, I want to return a list of data to the original caller.
...and my understanding is that UI stuff has to happen in the main thread...
No, that's not necessarily true. If you want to modify, let's say, a label from a background thread, you can do:
MethodInvoker m = () => { label1.Text = "Another text"; };
if (InvokeRequired) {
BeginInvoke(m);
}
else {
m.Invoke();
}
instead of:
label1.Text = "Another text";
If you are carefully with that, you won't have any cross-thread problems.
Edit:
Take a look at the answer I provided in the question Client Coding error for chatting application if you want to refactor a little bit that code.
Edit 2:
According to your edit, this is how I would do that:
Receive the urls from the user
Start each browser and a variable that will hold how many tabs you opened.
Start a progressbar or something that tells the user the application is working...
Everytime you receive a DocumentComplete event, you decrease the variable.
If the value of the variable is 0, then all pages were loaded.
Stop progress bar
Return the data you want to the user.
Now.. when will you need to use the Invoke? Everytime you want to access to some UI control from the background thread. In that case, when you're stopping the progress bar...
I am not sure I've got you right. Anyway, take a look at this:
How to update the GUI from another thread in C#?
I am displaying dialog on the main form, now one for loop is working behind but when that dialog will displayed code execution will be stopped, but I don't want to let stop the execution of the code, is any other any way to do that ?
EDIT: I am using now thread to do that and my code is like
Thread t;
private void StartParsingByLoop()
{
t = new Thread(new ThreadStart(RunProgress));
t.Start();
for (int i = 0; i < dialog.FileNames.Length; i++)
{
cNoteToParsed.AllContrctNotesFilePath.Add(dialog.FileNames[i].ToString());
}
cNoteToParsed.ParseContractNote();
if (cNoteToParsed.NOfContrctNoteParsed > 0)
LoadTransactionsInDataGridView();
t.Abort();
}
private void RunProgress()
{
frmProgress progressForImportingTran = new frmProgress("Importing Transactions", "ok");
progressForImportingTran.ShowDialog();
}
Now I have problem is that the dialog that shows the progress does not behave like dialog and gives access of the main form and if we try to access the main form then dialog goes to hide. And I dont want to make the dialog be hide.
You can let a different thread handle the loop.
Response to edit: Can you provide more details, perhaps some code? what is the loop doing? what form do you display?
(this answer is based on the assumption that we are taking about a winforms app)
Show the form using the Show method, rather than ShowDialog. By passing a reference to the main form, the dialog will stay on top of the main form even if it is not modal:
TheDialog dialog = new TheDialog();
dialog.Show(this);
Note though that the user can still interact with the controls on the main form, so you might want to disable some controls, depending on your scenario.
You state in your question that there are requirements that prevent you from using threading for this. This kind of requirement strikes me as odd, and it is a pity because this is one of the typical scenarios when you would want to use some sort of asynchronous construct. By performing heavy work on the UI thread, you get some drawbacks, including:
The UI will not be responsive - if you want to allow the user to cancel the work by clicking a button, that will be tricky to achieve in a robust manner.
The UI will not redraw properly since the UI thread is busy performing other work.
Do not use ShowDialog() but Show() to display the dialog windows. Show() will immediately return to the caller whereas ShowDialog() will hold the execution. Note that when using Show() your dialogs won't be modal anymore.
OK, so you don't need modal dialog, you need a mechanism for your user not to be able to select your main form while processing is enabled.
Modal dialog doesn't mean that execution is stopped - it just happens somewhere else.
There are several methods to do this, and you ruled out new thread creation (don't know why, it would solve it elegantly). If don't use thread, you'll have another problem - your processing should be done in CHUNKS and every little while you'll have to do something like Application.DoEvents() to enable your application to process messages and not be frozen to the user.
So, if you can create a method in the main form that shows your 'please wait' dialog which will perform some work and later do some more until is finished, you can do this:
create a new form (wait dialog)
start a timer inside of it and wire timer to the PARENT form
timer interval should be 1
ShowDialog() the form
on timer event do small amount of work (don't allow it to go more then 1/10 of seconds)
Can you do that?
Anyway:
task can't be split into small workable pieces
you can't use threads
you want your UI to be responsive
PICK 2. You can't have all 3 in Winforms.
I have a C# windows forms application. The way I currently have it set up, when Form1_Load() runs it checks for recovered unsaved data and if it finds some it prompts the user if they want to open that data. When the program runs it works alright but the message box is shown right away and the main program form (Form1) does not show until after the user clicks yes or no. I would like the Form1 to pop up first and then the message box prompt.
Now to get around this problem before I have created a timer in my Form, started the timer in the Form1_Load() method, and then performed the check and user prompt in the first Timer Tick Event. This technique solves the problem but is seems like there might be a better way.
Do you guys have any better ideas?
Edit: I think I have also used a background worker to do something similar. It just seems kinda goofy to go through all the trouble of invoking the method to back to the form thread and all that crap just to have it delayed a couple milliseconds!
I would use Form1_Shown()
Use the Shown event. It seems to suit what you need, and will only display the first time the form is shown.
Form f1 = new Form();
f1.Shown += new EventHandler(f1_Shown);
public void f1_Shown(object sender, EventArgs e)
{
// Show dialog in here
}
Try the "Shown" event:
Form.Show Event
Using a Windows.Forms.Timer is a good, stable, well-known, and easily understood technique for doing what you want. I would avoid any other timer objects.
The form's Shown event works well.
Overload / override the Show method. (My preferred technique for greater control.) In this method, I would do the checking needed. When ready, I would call the base.Show method, then do any other processing, such as message boxes, prompts, logging, or whatever.