Search code examples
c#serializationbackgroundworkerflowdocumentxamlwriter

Creating FlowDocument on BackgroundWorker thread


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?


Solution

  • 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 :)