Application.Run(Form) vs. Form.Show()? - c#

Suppose a lot of stuff is happening on the main GUI thread (data flowing in, user actions, etc.). Suppose we would like to create a form and show it.
Could there be a performance boost if we use Application.Run(Form) as opposed to Form.Show()? Is there a better way to do this? Please explain why or why not.

Do not use Application.Run() unless you know what it does. And, once you know what it does, you'll know why you shouldn't use it except at the beginning of the program.
Application.Run starts a message pump. This is the heart of any GUI program, and what enables the window to recieve messages, which allows it to fire events and do stuff. You cannot have two message pumps, as that doesn't make any sense.
(Yes, I know that you can have two message pumps, but why would you ever want to? It's hard enough having one pump!)
As to your real question (how do I not do stuff on my GUI thread), that's a bit more complicated. The simplest answer is "use threads". since I don't know the particulars of your situation, I can only give some general advice:
Do not try to manipulate controls from other threads. At best, it won't work. At worst, it will set your house on fire (okay, maybe not that bad. But, don't do it.). Instead, you need to Invoke methods. An example will be provided below.
Do not run long running processes on your GUI thread. Short things are okay, but anything that might take longer than half a second are probably best offloaded to another thread.
Use events to communicate from your Worker thread back to your GUI thread.
Here is an example of how to run a worker thread:
delegate void VoidDelegate();
List<int> results;
bool cancelWork = false;
void DoWork() {
int calc;
results = new List<int>();
for(int i = int.MinValue ; i < int.MaxValue; i+=10) {
if(cancelWork) break;
results.Add(i);
}
this.Invoke(new VoidDelegate(WorkFinished));
}
void Button1_Click(object sender, EventArgs e) {
button1.Enabled = false;
button2.Enabled = true;
cancelWork = false;
Thread t = new Thread(DoWork);
t.Start();
}
void Button2_Click(object sender, EventArgs e) {
button2.Enabled = false;
cancelWork = true;
}
void WorkFinished() {
button1.Enabled = true;
button2.Enabled = false;
textBox1.Text = results.Count.ToString();
}
Obviously, this is a contrived example, however it serves my purpose.
This hypothetical form contains two buttons, button1 ("Run") and button2 ("Cancel"), and a text box, textbox1. button2 should start out disabled (Enabled = false).
While the worker thread it running, the user can interact with any other controls, including the "Cancel" button (button2 in my example). Once it finishes, it Invokes the WorkFinished function, which displays the results (and otherwise cleans up state).

If you're referring to something that is not your app's main form then the answer would be no. Application.Run() initializes the main message loop and should be called just once when the application starts. All other forms shown during the lifetime of the app should be Form.Show or equivalent.
You can call Application.Run from another thread, but then you'll have two UI threads you need to synchronize anyway. You can't just pass stuff around without ensuring that you won't get into a contention situation.

Related

Backgroundworker does not perform cancel operation [duplicate]

I am facing an issue with communication between threads in a C#.NET application.
Hope someone will guide me in the right direction about the possible solutions.
I have an application in C#.NET.It is a windows form application.
My application has two threads - One thread is the main thread (UI thread) and the other one is the child thread. Lets call the child thread the "workerThread"
There is only one form used in the application.Lets call this form the "MainForm"
The child thread is started when the MainForm loads (used the form's "Load" event handler to start the thread)
In the MainForm class, I have a variable named "stopWork" which is a public boolean variable and it serves as a flag to indicate whether the child thread should continue working or should it stop
I have another class (besides the MainForm class) which contains the method that I execute in the the child thread. Lets call this second class the "WorkerClass".
I pass a reference to the current form (the MainForm) into the constructor of the "WorkerClass"
I have a button "stop" in the main form which sets "stopWork" to "true" if its clicked and then calls "workerThread.Join()" to wait for the child thread to finish excecution.
In the child thread, the method "doWork" keeps checking the status of "parentForm.stopWork" inside a for loop. If "stopWork" is set to "true" then the loop breaks and subsequently the method ends.
Now, the issue is, once I am clicking the "stop" button ,the application hangs.
I am pasting parts of the code below so that it is easier to understand :
public partial class MainForm : Form
{
Thread workerThread = null;
ThreadStart workerThreadStart = null;
WorkerClass workerClass = null;
public bool stopWork = true;
/*.......... some code ............*/
private void MainForm_Load(object sender, EventArgs e)
{
workerThreadStart = new ThreadStart(startWork);
workerThread = new Thread(workerThreadStart);
stopWork = false;
workerThread.Start();
}
private void startWork()
{
workerClass = new WorkerClass(this);
}
private void buttonStop_Click(object sender, EventArgs e) //"stop" button
{
if (workerThread != null)
{
if (workerThread.IsAlive == true)
{
stopWork = true;
workerThread.Join();
}
}
}
/*.......... some more code ............*/
}
public class WorkerClass
{
MainForm parentForm=null;
/*......... some variables and code ........*/
public WorkerClass(MainForm parentForm)
{
this.parentForm=parentForm;
}
/* .............. some more code ...........*/
public void doWork()
{
/*.......... some variables and code ...........*/
for(int i=0;i<100000;i++)
{
// ** Here is the check to see if parentForm has set stopWork to true **
if(parentForm.stopWork==true)
break;
/*......... do some work in the loop ..........*/
}
}
/********* and more code .........*/
}
I think I may know where the problem lies.
The problem is in the "doWork" method in the child thread trying to access "stopWork" variable in the parent form when already the parent form is blocked by calling the "workerThread.Join()" method. So ,I think this is a "deadlock" problem.
Am I right in identifying the problem ? Or am I wrong and the problem lies somewhere else ?
In case this is indeed a deadlock, what are the possible solutions to solve this ?
I did a bit of googling and found lots of resources on thread synchronisation and how to avoid deadlocks. But I could not understand how to apply them specifically to my problem.
I would really appreciate any help or guidance on resolving this issue.
Yes, the code you wrote is highly vulnerable to deadlock. The BackgroundWorker class is especially prone to cause this kind of deadlock.
The problem is located in code we can't see in your snippet, the WorkerClass. You are surely doing something there that affects the UI in one way or another, always the primary reason to consider creating a thread in the first place. You probably use Control.Invoke() to have some code run on the UI thread and update a control. Perhaps also to signal that the worker thread is completed and, say, set the Enable property of a button back to true.
That's deadlock city, such code cannot run until the UI thread goes idle, back to pumping its message loop. It will never be idle in your case, it is stuck in Thread.Join(). The worker thread can't complete because the UI thread won't go idle, the UI thread can't go idle because the worker thread isn't finishing. Deadlock.
BackgroundWorker has this problem too, the RunWorkerCompleted event cannot run unless the UI thread is idle. What you need to do is not block the UI thread. Easier said than done, BGW can help you get this right because it runs an event when it completes. You can have this event do whatever you now do in the code past the Thread.Join() call. You'll need a boolean flag in your class to indicate that you are in the 'waiting for completion' state. This answer has relevant code.
Use a BackgroundWorker for this task instead. When you want to stop the task's execution, call the background worker's CancelAsync method.
Generally speaking, rolling your own threading code (on any platform) is a recipe for disaster if you don't have an expert-level understanding of multithreading (and even then it's still dangerous).

Different results when passing a MetroFramework.Forms.MetroForm as Argument to a Method [duplicate]

UPDATE: Just to summarize what my question has boiled down to:
I was hoping that constructing .NET forms and controls did NOT create any window handles -- hoping that process was delayed until Form.Show/Form.ShowDialog
Can anyone confirm or deny whether that is true?
I've got a large WinForms form with tab control, many many controls on the form, that pauses while loading for a couple seconds. I've narrowed it down to the designer generated code in InitializeComponent, rather than any of my logic in the constructor or OnLoad.
I'm well aware that I can't be trying to interact with the UI on any thread other than the main UI thread, but what I'd like to do is to have the application pre-load this form (run the constructor) in the background, so it's ready for display on the UI thread instantly as soon as the user wants to open it. However, when constructing in a background thread, on this line in the designer:
this.cmbComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
I'm getting the error
Current thread must be set to single
thread apartment (STA) mode before OLE
calls can be made. Ensure that your
Main function has STAThreadAttribute
marked on it.
Now this is halfway down the designer file, which gives me hope that in general this strategy will work. But this particular line seems to be trying to instantly kick off some kind of OLE call.
Any ideas?
EDIT:
I think I'm not making myself clear here. The delay seems to take place during the construction of a bazillion controls during the designer-generated code.
My hope was that all this initialization code took place without actually trying to touch any real Win32 window objects since the form hasn't actually been shown yet.
The fact that I can set (for example) Label texts and positions from this background thread gave me hope that this was true. However it may not be true for all properties.
While it is not possible to create a form on one thread, and display it using another thread, it is certainly possible to create a form in a non main GUI thread. The current accepted answer seems to say this is not possible.
Windows Forms enforces the Single Threaded Apartment model. In summary this means that there can only be one Window message loop per thread and vice versa. Also, if for example threadA wants to interact with the message loop of threadB, it must marshal the call through mechanisms such as BeginInvoke.
However, if you create a new thread and provide it with it's own message loop, that thread will happily process events independently until it is told to end the message loop.
So to demonstrate, below is Windows Forms code for creating and displaying a form on a non GUI thread:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
label1.Text = Thread.CurrentThread.ManagedThreadId.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
ThreadStart ts = new ThreadStart(OpenForm);
Thread t = new Thread(ts);
t.IsBackground=false;
t.Start();
}
private void OpenForm()
{
Form2 f2 = new Form2();
f2.ShowDialog();
}
}
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
label1.Text = Thread.CurrentThread.ManagedThreadId.ToString() ;
}
}
The OpenForm method runs in a new thread and creates an instance of Form2.
Form2 is actually given it's own separate message loop by calling ShowDialog(). If you were to call Show() instead, no message loop would be provided and Form2 would close immediately.
Also, if you try accessing Form1 within OpenForm() (such as using 'this') you will receive a runtime error as you are trying to do cross-thread UI access.
The t.IsBackground=false sets the thread as a foreground thread. We need a foreground thread because background threads are killed immediately when the main form is closed without first calling FormClosing or FormClosed events.
Apart from these points, Form2 can now be used just like any other form. You'll notice that Form1 is still happily running as usual with it's own message lopp. This means you can click on the button and create multiple instances of Form2, each with their own separate message loop and thread.
You do need to be careful about cross Form access which is now actually cross-thread. You also need to ensure that you handle closing of the main form to ensure any non main thread forms are closed correctly.
I think your understanding is a little off. Controls must be touched from the thread that created them, not the main UI thread. You could have numerous UI threads in a application, each with its own set of controls. Thus creating a control on a different thread will not allow you to work with it from the main thread without marshalling all of the calls over using Invoke or BeginInvoke.
EDIT
Some references for multiple UI threads:
MSDN on Message Loops
MSDN social discussion
Multiple threads in WPF
The answer is no.
If you create a window handle on any thread other than the GUI thread you can never show it.
Edit: It is completely possible to create Forms and controls and
display them in a thread other than the main GUI thread. Of course if
you do this you can only access the multi threaded GUI from the thread
that created it, but it is possible. – Ashley Henderson
You need to perform any heavy lifting on a bg thread and then load the data into you GUI widget
In general, properties of the form need to be accessed from the same thread running the message loop. That means, in order to construct the form on another thread, you would need to marshal any calls to actually set properties using BeginInvoke. This is true for property sets from the constructor, too, if they end up generating a message that needs to be processed (as is happening to you now).
Even if you get that to work, what does it buy you? It will be a bit slower, not faster, overall.
Perhaps just show a splash screen while this form is loading?
Alternatively, review why your form takes so long to construct in the first place. It's not common for this to take seconds.
I believe it is possible to add the components created on the non-UI thread to the main UI, I've done it.
So there are 2 threads, 'NewCompThread' and 'MainThread'.
You spin off NewCompThread and it creates components for you - all ready to be displayed on the MainUI (created on MainThread).
But ... you WILL get an exception if you try something like this on NewCompThread:
ComponentCreatedOnNewCompTHread.parent = ComponentCreatedOnMainThread;
But you can add this:
if (ComponentCreatedOnMainThread.InvokeRequired) {
ComponentCreatedOnMainThread.Invoke(appropriate delegate...);
} else {
ComponentCreatedOnNewCompTHread.parent = ComponentCreatedOnMainThread;
}
And it will work. I've done it.
The strange thing (to me) is that then the ComponentCreatedOnNewCompTHread 'thinks' it was created on the MainThread.
If you do the following from the NewCompThread:
ComponentCreatedOnNewCompTHread.InvokeRequired
it will return TRUE, and you'll need to create a delegate and use Invoke to get back to the MainThread.
Creating a control in a background thread is possible but only on an STA thread.
I created an extension method in order to use this with the async/await pattern
private async void treeview1_AfterSelect(object sender, TreeViewEventArgs e)
{
var control = await CreateControlAsync(e.Node);
if (e.Node.Equals(treeview1.SelectedNode)
{
panel1.Controls.Clear();
panel1.Controls.Add(control);
}
else
{
control.Dispose();
}
}
private async Control CreateControlAsync(TreeNode node)
{
return await Task.Factory.StartNew(() => CreateControl(node), ApartmentState.STA);
}
private Control CreateControl(TreeNode node)
{
// return some control which takes some time to create
}
This is the extension method. Task does not allow to set the apartment so I use a thread internally.
public static Task<T> StartNew<T>(this TaskFactory t, Func<T> func, ApartmentState state)
{
var tcs = new TaskCompletionSource<T>();
var thread = new Thread(() =>
{
try
{
tcs.SetResult(func());
}
catch (Exception e)
{
tcs.SetException(e);
}
});
thread.IsBackground = true;
thread.SetApartmentState(state);
thread.Start();
return tcs.Task;
}

C# While loop thread.sleep in GUI

I have a while loop inside a delegate method that is called. My issue is that while it is inside the while loop the whole GUI is frozen until it exits the while loop. How can I make it so it does not freeze the GUI? Thanks
if (!IsUploadingAvailable())
{
MessageBox.Show("Uploading is not available, please wait until it is ready!", "Upload not available");
myButton.Enabled = false;
while (IsUploadingAvailable())
{
Thread.Sleep(RandomAmountOfTime(10000));
}
MessageBox.Show("Uploading is now available!");
}
You are blocking the GUI thread. You need to do your work on a different thread, e.g. by using the BackgroundWorker class.
You should use Timer
Try This:
System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();
timer1.Interval=10000;//10 seconds
timer1.Tick += new System.EventHandler(timer1_Tick);
if (!IsUploadingAvailable())
{
MessageBox.Show("Uploading is not available, please wait until it is ready!", "Upload not available");
myButton.Enabled = false;
while (IsUploadingAvailable())
{
timer1.Start();
}
MessageBox.Show("Uploading is now available!");
}
private void timer1_Tick(object sender, EventArgs e)
{
//do whatever you want
timer1.Stop();
}
As answered here :- Thread sleep in a for loop
You are blocking the UI thread - no updates will usually show until
you leave the event-handler. A hacky approach is to use
Application.DoEvents(), but this is lazy and risks re-entrancy
especially if you are pausing.
A better approach is to do the work on a background thread, and use
Invoke to push the data to the UI (don't talk to the UI from the
worker thread).
One of the most important things that differentiates a “quick and dirty” application from one that has been designed well is how the application’s user interface behaves during lengthy operations. The quick-and-dirty approach is to just do all of your work in a button’s Click event handler and not worry about the user interface. The problem with this is that the GUI will freeze up while the application does whatever work it needs to do.
A well designed application, on the other hand, is one that is careful to do as much work as possible in background threads, keeping the GUI responsive and making sure that it makes it obvious to the user that work is going on in the background and adjusts the GUI to disallow any user actions that don’t apply until after the work finishes.
BackgroundWorker was designed for exactly this kind of scenario.
See MSDN Or use the Task Parallel Library
Use Task:
if (!IsUploadingAvailable())
{
MessageBox.Show("Uploading is not available, please wait until it is ready!", "Upload not available");
myButton.Enabled = false;
await WaitForUploadingAvailable();
MessageBox.Show("Uploading is now available!");
}
async Task WaitForUploadingAvailable()
{
await Task.Run(() =>
{
while (!IsUploadingAvailable())
{
Thread.Sleep(RandomAmountOfTime(10000));
}
});
}
Declare the method that contains your code as async.

Multi-threading calls in Windows Forms application?

I'm trying to make my C# application multi threaded because sometimes, I get an exception that says I have made a call to a thread in an unsafe manner. I've never done any multi-threading before in a program, so bear with me if I sound kinda ignorant on the issue.
The overview of my program is that I want to make a performance monitoring applicaiton. What this entails is using the process and performance counter class in C# to launch and monitor an application's processor time, and sending that number back to the UI. However, in the method that actually calls the performance counter's nextValue method (which is set to perform every second thanks to a timer), I would sometimes get the aforementioned exception that would talk about calling a thread in an unsafe manner.
I've attached some of the code for your perusal. I know this is kind of a time consuming question, so I'd be really grateful if anyone could offer me any help as to where to make a new thread and how to call it in a safe way. I tried looking at what was up on MSDN, but that just kinda confused me.
private void runBtn_Click(object sender, EventArgs e)
{
// this is called when the user tells the program to launch the desired program and
// monitor it's CPU usage.
// sets up the process and performance counter
m.runAndMonitorApplication();
// Create a new timer that runs every second, and gets CPU readings.
crntTimer = new System.Timers.Timer();
crntTimer.Interval = 1000;
crntTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
crntTimer.Enabled = true;
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
// get the current processor time reading
float cpuReading = m.getCPUValue();
// update the current cpu label
crntreadingslbl.Text = cpuReading.ToString(); //
}
// runs the application
public void runAndMonitorApplication()
{
p = new Process();
p.StartInfo.UseShellExecute = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = fileName;
p.Start();
pc = new System.Diagnostics.PerformanceCounter("Process",
"% Processor Time",
p.ProcessName,
true);
}
// This returns the current percentage of CPU utilization for the process
public float getCPUValue()
{
float usage = pc.NextValue();
return usage;
}
Check out Jon Skeet's article on multi-threading, particularly the page on multi-threading winforms. It should fix you right up.
Basically you need to check to see if an invoke is required, and then perform the invoke if needed. After reading the article you should be able to refactor your UI-updating code into blocks that look like this:
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
// get the current processor time reading
float cpuReading = m.getCPUValue();
if (InvokeRequired)
{
// We're not in the UI thread, so we need to call BeginInvoke
BeginInvoke(new Action(() => crntreadingslbl.Text = cpuReading.ToString()));
return;
}
// Must be on the UI thread if we've got this far
crntreadingslbl.Text = cpuReading.ToString();
}
In your code, an invoke will be required because you are using a Timer. According to the documentation for System.Timers.Timer:
The Elapsed event is raised on a ThreadPool thread.
This means that the OnTimedEvent() method that you set as the Timer's delegate will execute on the next available ThreadPool thread, which will definitely not be your UI thread. The documentation also suggests an alternate way to solve this problem:
If you use the Timer with a user
interface element, such as a form or
control, assign the form or control
that contains the Timer to the
SynchronizingObject property, so that
the event is marshaled to the user
interface thread.
You may find this route easier, but I haven't tried it.
Your problem, I think, is that this line:
crntreadingslbl.Text = cpuReading.ToString();
Is running outside of the UI thread. You cannot update a UI element outside of the UI thread. You need to call Invoke on the Window to call a new method on the UI thread.
All that said, why not use perfmon? It's built for purpose.
The BackGroundWorker component may help you. It is available on the toolbox so you can drag to your form.
This component exposes a set of events to execute tasks in a thread different than the UI thread. You don't have to worry about creating a thread.
All the interaction between the code running on background and the UI controls must be done via the event handlers.
For your scenario you can setup a timer to trigger the background worker at a specific interval.
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
Then you implement the proper event handlers to actually collect data and update the UI
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Collect performance data and update the UI
}

In C#, wait on the mainthread while continuing to process UI updates? (.NET 2.0 CF)

I want to otherwise block code execution on the main thread while still allowing UI changes to be displayed.
I tried to come up with a simplified example version of what I'm trying to do; and this is the best I could come up with. Obviously it doesn't demonstrate the behavior I'm wanting or I wouldn't be posting the question. I just hope it gives some code context to back my poor explanation of the problem I'm hoping to solve.
Within a button click handler on a form I have this:
private void button2_Click(object sender, EventArgs e)
{
AutoResetEvent autoResetEvent = new AutoResetEvent(false);
new Thread(delegate()
{
// do something that takes a while.
Thread.Sleep(1000);
// Update UI w/BeginInvoke
this.BeginInvoke(new ThreadStart(
delegate() {
this.Text = "Working... 1";
this.Refresh();
Thread.Sleep(1000); // gimme a chance to see the new text
}));
// do something else that takes a while.
Thread.Sleep(1000);
// Update UI w/Invoke
this.Invoke(new ThreadStart(
delegate() {
this.Text = "Working... 2";
this.Refresh();
Thread.Sleep(1000); // gimme a chance to see the new text
}));
// do something else that takes a while.
Thread.Sleep(1000);
autoResetEvent.Set();
}).Start();
// I want the UI to update during this 4 seconds, even though I'm
// blocking the mainthread
if (autoResetEvent.WaitOne(4000, false))
{
this.Text = "Event Signalled";
}
else
{
this.Text = "Event Wait Timeout";
}
Thread.Sleep(1000); // gimme a chance to see the new text
this.Refresh();
}
If I didn't set a timout on the WaitOne() the app would deadlock on the Invoke() call.
As to why I'd want to do this, I've been tasked with moving one subsystem of an app to do work in a background thread, but still have it block user's workflow (the main thread) only sometimes and for certain types of work related to that subsystem only.
You want to use the "BackgroundWorker" class, which will take most of this pain out of this for you.. but as mentioned before, you'll also want to structure it so that the main thread is updating the UI and the worker is doing the heavy lifting.
It is easyer then you might think.
Suggestion: when you need a thread to perform some occasional work, get it from the threadpool, so you will not need strange/error prone recycling code.
When you want something on another thread to update your UI, you just need a reference to the form and to call Form.Invoke passing the UI code you want the main thread to execute; it's a best pactice, in an event, to release the UI thread as soon as possible.
Ie:
private void button1_Click(object sender, EventArgs e)
{
// this is the UI thread
ThreadPool.QueueUserWorkItem(delegate(object state)
{
// this is the background thread
// get the job done
Thread.Sleep(5000);
int result = 2 + 2;
// next call is to the Invoke method of the form
this.Invoke(new Action<int>(delegate(int res)
{
// this is the UI thread
// update it!
label1.Text = res.ToString();
}), result);
});
}
Hope this helps you:)
EDIT: I am sorry, I didn't read the "blocking user workflow" part.
WindowsForms is not designed to do that, blocking the main thread is BAD (it handles the messages from the OS).
You don't have to block the user workflow via freezing a form (which would then be considered "Not Responding" by windows), the way to block user workflow is by disabling any control you want (with the Invoke method above if from another thread), even the entire form!!
Common activities which 'block' the main thread are things like opening messages boxes or modal dialog. The main code appears to block at the MessageBox or ShowDialog call.
The way those items work (and MessageBox is just a specialized modal dialog) is that they contain their own message pump while they're blocking.
Although it's a nasty hack, you can do something like this in your app by looping calling Application.DoEvents() to keep the user messages pumping while you're waiting for your other task to complete. You need to be careful because all sorts of nasty things might lead from pumping messages like this - for example someone close the form or reenter your current message handler - the modal dialogs avoid this by effectively disabling input from the form which launches them.
I did mean to say that BackgroundWorker is a better solution, if you can make it fit. I sometimes combine it with a modal 'progress dialog' to give me the background thread / message pumping and the blocking of the UI thread.
Edit - to expand on the last bit:
One approach I've used is to have a 'progress form' class, which takes a BackgroundWorker object as a constructor parameter, and contains handlers for the progress and completion events of the background worker which gets passed to it.
The form which wants the work done creates the background worker and hooks up the 'work' event (can't remember what it's called right now), and then creates a progress dialog to which it passes the background worker. It then modally shows the progress dialog, which means it will wait (but pumping messages) until the progress dialog closes.
The progress form is responsible for starting the BackgroundWorker from its OnLoad override, and closes itself when it sees the BackgroundWorker complete. Obviously you can add message text, progress bars, cancel buttons, whatever to the progress form.
structure your app so that the main thread only performs UI updates, and all other work is done on secondary threads via a work queue; then add a waiting-for-godot flag to your main thread and use it to guard the method that adds items to the work queue
out of curiosity: why do you want to do this?
You should probably restructure your code as others have suggested, but depending on the behavior you're looking for, you might also want to have a look at using a Thread.Join on your background worker thread. Join actually allows the calling thread to process COM and SendMessage events while it waits for the other thread to finish. This seems like it could be dangerous in come cases, but I've actually had a couple scenarios where it was the only way to wait for another thread to finish cleanly.
Thread..::.Join Method
Blocks the calling thread until a
thread terminates, while continuing to
perform standard COM and SendMessage
pumping.
(from http://msdn.microsoft.com/en-us/library/95hbf2ta.aspx)
I agree with the others that are suggesting you use Background Worker. It does the heavy lifting and allows the UI to continue. You can use the Report Progress of Background Worker to initiate times where the Main Form can be set to be disabled while it performs the actions in the background and then re-enable once the 'certain instances' have completed processing.
Let me know if this helps!
JFV
If you could adjust your code so that you set a flag once a process has begun and then check that in the UI before you start an additional operation I think you'd have a much easier time coding this. I would create a delegate that could be called from the thread in the threadpool or user created thread to update on progress in the UI. Once the background process has been completed switch the flag and now normal UI operations can continue. The only caveat you need to be aware of is that when you update UI components you must do it on the thread they were created on, the main/UI thread. In order to accomplish this you can call the Invoke() method on any control that lives on that thread and pass it the delegate and parameters you need to call it.
Here's a link to a tutorial I wrote some time ago about how to use Control.Invoke():
http://xsdev.net/tutorials/pop3fetcher/
Just a code snippet: don't have much time sorry :)
private void StartMyDoSomethingThread() {
Thread d = new Thread(new ThreadStart(DoSomething));
d.Start();
}
private void DoSomething() {
Thread.Sleep(1000);
ReportBack("I'm still working");
Thread.Sleep(1000);
ReportBack("I'm done");
}
private void ReportBack(string p) {
if (this.InvokeRequired) {
this.Invoke(new Action<string>(ReportBack), new object[] { p });
return;
}
this.Text = p;
}
It is best to dispatch the work but if you must, maybe something like this. Just call this method to wait for the signal rather than calling the waitone.
private static TimeSpan InfiniteTimeout = TimeSpan.FromMilliseconds(-1);
private const Int32 MAX_WAIT = 100;
public static bool Wait(WaitHandle handle, TimeSpan timeout)
{
Int32 expireTicks;
bool signaled;
Int32 waitTime;
bool exitLoop;
// guard the inputs
if (handle == null) {
throw new ArgumentNullException("handle");
}
else if ((handle.SafeWaitHandle.IsClosed)) {
throw new ArgumentException("closed wait handle", "handle");
}
else if ((handle.SafeWaitHandle.IsInvalid)) {
throw new ArgumentException("invalid wait handle", "handle");
}
else if ((timeout < InfiniteTimeout)) {
throw new ArgumentException("invalid timeout <-1", "timeout");
}
// wait for the signal
expireTicks = (int)Environment.TickCount + timeout.TotalMilliseconds;
do {
if (timeout.Equals(InfiniteTimeout)) {
waitTime = MAX_WAIT;
}
else {
waitTime = (expireTicks - Environment.TickCount);
if (waitTime <= 0) {
exitLoop = true;
waitTime = 0;
}
else if (waitTime > MAX_WAIT) {
waitTime = MAX_WAIT;
}
}
if ((handle.SafeWaitHandle.IsClosed)) {
exitLoop = true;
}
else if (handle.WaitOne(waitTime, false)) {
exitLoop = true;
signaled = true;
}
else {
if (Application.MessageLoop) {
Application.DoEvents();
}
else {
Thread.Sleep(1);
}
}
}
while (!exitLoop);
return signaled;
}
I went with something I haven't seen posted yet which is to use MessageQueues.
The MainThread blocks while waiting for the next message on a queue.
The background thread posts different types of messages to the MessageQueue.
Some of the message types signal the MainThread to update UI elements.
Of course, there is a message to tell the MainThread to stop blocking and waiting for messages.
Seems over the top considering the windows message loop already exists somewhere, but it works.

Categories

Resources