Reputation: 1484
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
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
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
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