silverspoon
silverspoon

Reputation: 1095

Form.Show() intermittently showing the form in a thread

I need to show a progress dialog while opening a file, which is a time consuming operation. To do so, I am using following inside my open file function:

           //some code
           ...
           ...
           ...
           bool done = false;

            //Show progress in a separate thread. 
            System.Threading.ThreadPool.QueueUserWorkItem((x) =>
            {
                using (var progressDialog = new ProgressDialog())
                {
                    progressDialog.TopMost = true;
                    progressDialog.Show();

                    while (!done)
                    {
                        if(progressDialog.Message != this.strProgressMsg)
                            progressDialog.Message = this.strProgressMsg;

                        Application.DoEvents();
                    }

                    progressDialog.Close();
                }
            });

           ....
           ....
           done = true;
           ....
           ....

The Problem: Progressbar dialog shows up some times and sometimes it doesn't. My file open function runs in the main thread. Can someone please point me in the right detection as why this might be happening?

Upvotes: 1

Views: 477

Answers (2)

Brian Gideon
Brian Gideon

Reputation: 48949

Forms need a message to work correctly. You can start a message loop with Application.Run or Form.ShowDialog both of which are blocking calls so obviously this strategy will not work very well in a worker thread. Here are two ways that you can get it to work correctly.

  • Create progressDialog on the UI thread and send messages to it via Control.Invoke to update it with new progress information.
  • Create progressDialog on the UI thread and have it poll a variable using a System.Windows.Forms.Timer for new progress information that the worker thread publishes.

In this case the polling method is preferred over of the Control.Invoke method because:

  • It breaks the tight coupling between the UI and worker threads that Control.Invoke imposes.
  • It puts the responsibility of updating the UI thread on the UI thread where it should belong anyway.
  • The UI thread gets to dictate when and how often the update should take place.
  • There is no risk of the UI message pump being overrun as would be the case with the marshaling techniques initiated by the worker thread.
  • The worker thread does not have to wait for an acknowledgement that the update was performed before proceeding with its next steps (ie. you get more throughput on both the UI and worker threads).

Upvotes: 1

cdhowie
cdhowie

Reputation: 169018

You have this backwards. Here's how it should work:

  1. Spin off a thread to open the file, and have it call back to the main thread using progressDialog.Invoke() to perform GUI updates. (It should not set strProgressMsg and wait for something else to notice the change -- have it send the update straight to the dialog.)
  2. Show the progress dialog modally from the main thread.

So something like this:

using (var progressDialog = new ProgressDialog()) {
    progressDialog.TopMost = true;

    System.Threading.ThreadPool.QueueUserWorkItem((x) =>
    {
        try {
            // this represents whatever loop you use to load the file
            while (...) {
                // do some work loading the file

                // update the status
                progressDialog.Invoke(new MethodInvoker(
                    () => progressDialog.Message = "Hello, World!"));
            }
        } finally {
            // done working
            progressDialog.Invoke(new MethodInvoker(progressDialog.Close));
        }
    });

    // this will block until the thread closes the dialog
    progressDialog.ShowDialog();
}

Upvotes: 5

Related Questions