4

I am working on a WPF application. I have a time consuming method that I want to run async via BackgroundWorker. While the method runs, I want to display a modal "Please Wait..." dialog window, which must automatically close when the BackgroundWorker completes.

I currently have very little experience with BackgroundWorker or any multi threaded programming.

The code below currently results in an InvalidOperationException, with the message "The calling thread must be STA, because many UI components require this."

Please advise me on how to achieve what I am trying to achieve, and extra brownie-points if you can help me understand what is going wrong.

Many thanks!

EDIT Just to clarify - The idea is that the main thread launches the BackgroundWorker, then shows the modal dialog. When the worker completes, it closes the modal dialog. When the modal dialog closes, the main thread continues.

public class ImageResizer
{
    private BackgroundWorker worker;
    private MemoryStream ImageData { get; set; } // incoming data
    private public MemoryStream ResizedImageData { get; private set; } // resulting data
    private Dialogs.WorkInProgressDialog ProgressDialog;


    // Public interface, called by using class:
    public MemoryStream ReduceImageSize(MemoryStream imageData)
    {
        // injected data:
        this.ImageData = imageData;

        // init progress dialog window:
        ProgressDialog = new Dialogs.WorkInProgressDialog();

        // Start background worker that asyncronously does work
        worker = new BackgroundWorker();
        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);            
        worker.RunWorkerAsync();

        // Show progress dialog. Dialog is MODAL, and must only be closed when resizing is complete
        ProgressDialog.ShowDialog(); // THIS LINE CAUSES THE INVALID OPERATION EXCEPTION

        // This thread will only continue when ProgressDialog is closed.

        // Return result
        return ResizedImageData;
    }

    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        // Call time consuming method
        ResizedImageData = ReduceImageSize_ActualWork();
    }

    // The actual work method, called by worker_DoWork
    private MemoryStream ReduceImageSize_ActualWork()
    {
        // Lots of code that resizes this.ImageData and assigns it to this.ResizedImageData
    }

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {   
        // Async work completed - close progress dialog
        ProgressDialog.Close();
    }
}
Pieter Müller
  • 4,573
  • 6
  • 38
  • 54
  • Moderators, please delete my question - the error I received was due to an unrelated coding error and thus this question is A) Nonsense and B) of no use to anyone. Thanks. – Pieter Müller Jan 09 '12 at 14:49

1 Answers1

2

You can't call ShowDialog from the BackgroundWorker. You have to use the Dispatcher to ask the UI thread to execute it:

 this.Dispatcher.BeginInvoke(new Action(() => ProgressDialog.ShowDialog()));

The 'Completed' event of the BackgroundWorker is executed in the UI thread, so this part should be fine.

Kevin Gosse
  • 38,392
  • 3
  • 78
  • 94
  • 1
    Also, since the UI thread will handle the ShowDialog() using Dispatcher.Invoke, is the ProgressDialog.Close() in the RunWorkerCompleted necessary or even legal? – Sabuncu Jan 09 '12 at 08:25
  • You're perfectly right. I misread the comments in the samples and somehow thought that the code had to continue only after the modal was closed. Replacing it by BeginInvoke. Then the Close is useful and legal since the modal dialog will still be open when the background worker finishes its work. – Kevin Gosse Jan 09 '12 at 08:29
  • Thanks @KooKiz, but I'm confused - in the code I provided, I am NOT calling ShowDialog from the BackgroundWorker - I invoke `worker.RunWorkerAsync();` and then `ProgressDialog.ShowDialog();`, both in the same method. – Pieter Müller Jan 09 '12 at 08:35
  • @Kookiz: But your original assumption was correct, the OP wants the background thread to only continue *after* the dialog is closed - see comment "// This thread will only continue when ProgressDialog is closed." in method ReduceImageSize. [Maybe the application needs to be restructured?] – Sabuncu Jan 09 '12 at 08:44
  • Yep, I've added a clarification in an edit - the Modal Dialog is launched from the main thread, and withholds the user from interaction with the main window while the background worker is busy. When the worker completes, the Modal Dialog is closed, and the main thread continues. – Pieter Müller Jan 09 '12 at 08:58
  • The weird part is: I just tried your code and it works... Where do you call the ReduceImageSize method? From which thread? – Kevin Gosse Jan 09 '12 at 10:11
  • That is very weird. I'm going to try it in a clean project and see if I get the same result. Thanks for your trouble, I'll post here as soon as it is tested. – Pieter Müller Jan 09 '12 at 11:02
  • You're right - it works in a clean project. So I need to find some other problem. Thanks. So this was a bit silly. Stay tuned. – Pieter Müller Jan 09 '12 at 11:13
  • @KooKiz I found the problem - there was a misplaced call to create the dialog window, inside the ActualWork method, that I did not see. My sincere apologies, and thanks for the time. I'm going to delete the question, since it isn't really a question and won't be of any help to anybody. – Pieter Müller Jan 09 '12 at 14:45
  • Looks like I can't delete a question that has an answer. – Pieter Müller Jan 09 '12 at 14:46