mjk6026
mjk6026

Reputation: 1484

BackgroundWorker and WPF

i made a 'FlowDocument', a WPF object, from DoWork of System.ComponentMode.BackgroundWorker but i can't access it in WPF UI thread.

using System;
using System.Windows;
using System.Windows.Documents;
using System.ComponentModel;
namespace WpfApplication1
{
    public partial class MainWindow : Window
    {

        BackgroundWorker bw = new BackgroundWorker();

        public MainWindow()
        {
            InitializeComponent();

            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.RunWorkerCompleted+=new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
            bw.RunWorkerAsync();
        }

        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {

            FlowDocument myFlowDocument = new FlowDocument();
            Paragraph myParagraph = new Paragraph();
            myParagraph.Inlines.Add(new Bold(new Run("Some bold text in the paragraph.")));
            myFlowDocument.Blocks.Add(myParagraph);

            e.Result = myFlowDocument;

        }

        private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            //runtime error occured here.
            fviewer.Document = (FlowDocument)e.Result;
        }

    }
}

i heared when i access a WPF object in another thread, i need to using dispatcher(). but RunWorkerCompleted() is not an another thread of UI so i confused. how can i access the myFlowDocument?

Upvotes: 3

Views: 950

Answers (3)

Scott Weinstein
Scott Weinstein

Reputation: 19117

I made a 'FlowDocument', a WPF object, from DoWork of System.ComponentMode.BackgroundWorker

Don't do that. UI Objects need to be created and updated from the UI thread.

Upvotes: 0

vcsjones
vcsjones

Reputation: 141678

As Bob Vale correctly points out; it's a general rule of thumb to never create UI objects on another thread. When you create presentation objects; you should do that on the UI thread. The background task should return simple data. I'd change the DoWork to look like this:

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        //Assume some kind of "work" is being done here.
        e.Result = "Some bold text in the paragraph";
    }

Then You could set the Document content's via the Dispatcher:

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Action<string> action = r => 
        {
            FlowDocument myFlowDocument = new FlowDocument();
            Paragraph myParagraph = new Paragraph();
            myParagraph.Inlines.Add(new Bold(new Run(r)));
            myFlowDocument.Blocks.Add(myParagraph);
            fviewer.Document = myFlowDocument;
        };
        Dispatcher.Invoke(action, (string)e.Result);
    }

In this case, what the Dispatcher is doing is allowing you to schedule work (a delegate in this case) against the thread that owns the UI.

Upvotes: 0

Bob Vale
Bob Vale

Reputation: 18474

The problem is because the FlowDocument is created in a different thread to UI thread.

You will need to create the flow document on the main UI thread. And then in your background worker you will have to use the flow documents Dispatcher.Invoke to set properties and create the items. In your simple example there is no real advantage to using a background worker. The worker should be used for handling your long running processes.

The only other way might be to create the document in your background worker, serialize it to an in memory stream and then deserialize once you've returned to the UI thread.

Upvotes: 2

Related Questions