質問

Like many applications, I want to update my status text when the application is doing long computation. I've read articles about Dispatcher and BackgroundWorker. I know I definitely need to make sure the the updates happen in the UI thread. My first try was:

MyView.UpdateStatus( "Please wait" );
LongComputation();
MyView.UpdateStatus( "Ready" );

This does not work because (I think) the LongComputation prevents the status being updated.

So I tried to do this:

BackgroundWorker worker = new BackgroundWorker();
MyView.UpdateStatus( "Please wait");
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    LongComputation();
}
worker.RunWorkerAsync();
MyView.UpdateStatus( "Ready" );

I was hopping that the extra thread will give the UpdateStatus a chance to update status text. It does not work either. One of the reason is that the result of the LongComputation is displayed in a Windows Form. As soon as I put the LongComputation inside the BackgroundWorker, the result doesn't show up.

So I tried a third time by using the flowing code:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate(object s, DoWorkEventArgs args)
{
    Dispatcher.Invoke(((Action)(() => Status.Text = args.Argument as string)));
};

worker.RunWorkerAsync(newStatus);

I was hoping that putting the update in a another thread would work. But it didn't.

What can I do to make sure that the status reflects the correct program state?

役に立ちましたか?

解決

BackgroundWorker uses the RunWorkerCompleted and the ReportProgress events to communicate back to the main thread. RunWorkerCompleted should do what you need to do as it will get executed on the UI thread as soon as the background work is complete.

        BackgroundWorker worker = new BackgroundWorker();

        worker.DoWork += delegate(object s, DoWorkEventArgs args)
        {
            LongComputation();
        };

        // RunWorkerCompleted will fire on the UI thread when the background process is complete
        worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
        {
            if (args.Error != null)
            {
                // an exception occurred on the background process, do some error handling
            }

            MyView.UpdateStatus("Ready");
        };

        MyView.UpdateStatus("Please wait");
        worker.RunWorkerAsync();

Additionally, you can use the RunWorkerCompleted to marshall results back to the main thread using the Result property of the DoWorkerEventArgs.

        worker.DoWork += delegate(object s, DoWorkEventArgs args)
        {
            args.Result = LongComputation();
        };

        worker.rep

        // RunWorkerCompleted will fire on the UI thread when the background process is complete
        worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
        {
            if (args.Error != null)
            {
                // an exception occurred on the background process, do some error handling
            }

            var result = args.Result;

            // do something on the UI with your result

            MyView.UpdateStatus("Ready");
        };

Finally, you can use the ReportProgress event you update your UI at logical steps in your background process:

        worker.DoWork += delegate(object s, DoWorkEventArgs args)
        {
            FirstHalfComputation();

            // you can report a percentage back to the UI thread, or you can send 
            // an object payload back
            int completedPercentage = 50;
            object state = new SomeObject();
            worker.ReportProgress(completedPercentage , state); 

            SecondHalfComputation();
        };

        worker.WorkerReportsProgress = true;    // this is important, defaults to false
        worker.ProgressChanged += delegate(object s, ProgressChangedEventArgs args)
        {
            int completedPercentage = args.ProgressPercentage;
            SomeObject state = args.UserState as SomeObject

            // update a progress bar or do whatever makes sense
            progressBar1.Step = completedPercentage;
            progressBar1.PerformStep();
        };

他のヒント

I solved my problem with storing the Dispatcher from the main thread at Load of the form and then calling the dispatcher from the member-variable I stored it in from the BackgroundWorker thread:

Declaring member-variable at the beginning of the form:

Dispatcher mDispatcherMain = null;

Storing the dispatcher at the Load-function of the form:

mDispatcherMain = Dispatcher.CurrentDispatcher;

Invoking the main thread from the BackgroundWorker's DoWork-function:

mDispatcherMain.Invoke(new Action(() => { /* What you want to do */ }));
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top