I have the need to generate dynamically generate a FlowDocument
from a large set of data. Because the process takes several minutes, I'd like to perform the operation on a background thread rather than have the UI hang.
However, I can't generate the FlowDocument
on a non-UI thread otherwise attempts to insert rectangles and images cause run-time errors complaining that it's not STA thread.
There are a couple of threads on StackOverflow which seem to involve the same problem I'm having:
In the first link someone suggests the following:
"What I'd do: use a XamlWriter
and serialize the FlowDocument
into an XDocument
. The serialization task involves the Dispatcher
, but once it's done, you can run as many wacky parallel analyses of the data as you want and nothing in the UI will affect it. (Also once it's an XDocument
you query it with XPath
, which is a pretty good hammer, so long as your problems are actually nails.)"
Can someone elaborate on what does the author mean by this?
For any future visitors
I faced the same problem and solved all thanks to this article
article
What ended up doing is creating the object on a background thread
Thread loadingThread = new Thread(() =>
{
//Load the data
var documant = LoadReport(ReportTypes.LoadOffer, model, pageWidth);
MemoryStream stream = new MemoryStream();
//Write the object in the memory stream
XamlWriter.Save(documant, stream);
//Move to the UI thread
Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
(Action<MemoryStream>)FinishedGenerating,
stream);
});
// set the apartment state
loadingThread.SetApartmentState(ApartmentState.STA);
// make the thread a background thread
loadingThread.IsBackground = true;
// start the thread
loadingThread.Start();
And then wrote the result in the memory stream as xaml so we can read it back in the main thread
void FinishedGenerating(MemoryStream stream)
{
//Read the data from the memory steam
stream.Seek(0, SeekOrigin.Begin);
FlowDocument result = (FlowDocument)XamlReader.Load(stream);
FlowDocumentScrollViewer = new FlowDocumentScrollViewer
{
Document = result
};
//your code...
Wish it could save others some time :)
While not realy an answer to elaborating on what the author of your quote means, maybe this can be a solution for your problem:
If you hook yourself into the Application.Idle Event, you can build your FlowDocument one by one there. This event still is in the UI Thread so you won't get problems like in a background worker.
Altho you have to be carefull not to do too much work at once, otherwise you will block your application.
If it is possible to seperate your generation process into small chunks you can process those chunks one by one in this event.