tags:

views:

92

answers:

7

I have a WinForm set up and a process that loops until a button is pressed on the form.

When I try to run my code, the form does not even display. I suspect this is because the code gets stuck in the loop and doesn't get far enough to display the WinForm. How can I get the form to display and the loop to run after that point?

A: 

You can use the form load event to trigger the start of the loop.

So it would handle the event Me.Load However is it necessary for your loop to be happening inside of the UI?

msarchet
would it be more proper to run the loop in a different thread (or... something else?) and have it communicate with the interface in the event of a change?
Soo
Yes, this would be the more appropriate way to do this.
msarchet
+3  A: 

If you're looping because you need to do something with the GUI periodically while waiting for input, I suggest using a Timer control and its Tick event.

If you want to do non-GUI things while waiting, a more traditional timer is better suited to the task,

R. Bemrose
Use `System.Timers.Timer`; it's far more usable than `System.Threading.Timer`.
SLaks
A: 

This happens because your loop is keeping the window function from processing messages, such as those that tell it to repaint itself. Place a call to Application.DoEvents() inside of your loop to allow the UI to continue to function.

However, you need to ask yourself why you're looping like this in the first place. If you're, say, copying a bunch of files, this might make sense. For most tasks, though, responding to a timer tick should do the trick and won't block the UI.

David Lively
+1  A: 

Your form loading is freezing because the UI of a windows form runs in a single thread. And the logic that you put on the Load event of this form is running on that thread.

You can run your loop on a separate thread easily by using a BackgroundWorker component on your windows form. On the event DoWork of your background worker, you place the code that has the loop that should run without block your UI. On the Form.Load event, you can start the background worker component by calling the method RunWorkerAsync. On the event handler of your button, you place a code to stop the background worker by calling CancelAsync method.

The article How to: Implement a Form That Uses a Background Operation shows exactly how to accomplish it.


About your comment on not being able to update the Text of a TextBox from a your background worker component. It happens because it is not allowed to modify the state of a windows forms control from a different thread (your background worker code is running on a separated thread) MSDN documentation says:

Access to Windows Forms controls is not inherently thread safe. If you have two or more threads manipulating the state of a control, it is possible to force the control into an inconsistent state. Other thread-related bugs are possible, such as race conditions and deadlocks. It is important to make sure that access to your controls is performed in a thread-safe way.

A sample of how you can update the state of your windows forms controls from your background thread will be similar to the one below (assuming that the new value is already stored on a String variable named text):

// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{   
  SetTextCallback d = new SetTextCallback(SetText);
  this.Invoke(d, new object[] { text });
}
else
{
  this.textBox1.Text = text;
}

I borrowed this code snipped from How to: Make Thread-Safe Calls to Windows Forms Controls article. It can provide you more information about how to deal with multi-threaded windows forms.

Carlos Loth
I am unable to modify text on my form from my background worker. The error states that the text field is being used by another thread.
Soo
You can do that by calling the Form.Invoke method. I'll update the answer with an example.
Carlos Loth
A: 

You should run your loop in a background thread using the BackgroundWorker component.

Remember that the background thread cannot directly interact with the UI controls.
To report the progress on the UI, you should call the BackgroundWorker's ReportProgress method in the background thread, and handle the ProgressChanged event to update the UI.

You can call the CancelAsync method when the Button is clicked, and loop until the CancellationPending property is true.

SLaks
+2  A: 

You should probably run the loop in a background thread. The BackgroundWorker class makes this pretty easy to do.

Steve Dennis
+2  A: 

Don't do that.

Windows Forms (like most modern user interface development toolkits) is an event-driven framework. You should never use a loop that "waits" for something to happen; instead you want to use an event that triggers something to happen.

Essentially what's happening is this: WinForms has a loop running a message pump that listens for events from Windows and triggers C# events in response to them. Your code is executing on the same thread as that message pump (it has to, since in WinForms only one thread is allowed to touch any given control). So if you put that thread into a loop, the WinForms code that should be pumping messages isn't, and your user interface appears to hang, since it isn't responding to any messages from Windows. (If you keep clicking it, you will fill up the message queue and get a dialog box that says "This application has stopped responding, do you want to terminate?" or something like that.)

The correct solution is to do one of the following:

Another solution that would work, but is not a good idea is:

Daniel Pryden
You forgot to mention an actual thread.
Blindy