3 Stimmen

BackgroundWorker und WPF

Ich habe ein 'FlowDocument', ein WPF-Objekt, aus dem DoWork von System.ComponentModel.BackgroundWorker erstellt, aber ich kann nicht darauf im WPF-UI-Thread zugreifen.

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)
        {
            // Laufzeitfehler trat hier auf.
            fviewer.Document = (FlowDocument)e.Result;
        }

    }
}

Ich habe gehört, dass wenn ich auf ein WPF-Objekt in einem anderen Thread zugreife, muss ich Dispatcher verwenden. Aber RunWorkerCompleted() ist kein anderer Thread des UI, also bin ich verwirrt. Wie kann ich auf das myFlowDocument zugreifen?

2voto

Bob Vale Punkte 17549

Das Problem besteht darin, dass das FlowDocument in einem anderen Thread als dem UI-Thread erstellt wird.

Sie müssen das Flow-Dokument im Haupt-UI-Thread erstellen. Und dann müssen Sie im Hintergrundarbeiter die Dispatcher.Invoke des Flow-Dokuments verwenden, um Eigenschaften festzulegen und Elemente zu erstellen. In Ihrem einfachen Beispiel gibt es keinen wirklichen Vorteil bei der Verwendung eines Hintergrundarbeiters. Der Arbeiter sollte für die Bearbeitung Ihrer lang laufenden Prozesse verwendet werden.

Der einzige andere Weg könnte sein, das Dokument im Hintergrundarbeiter zu erstellen, es in einen In-Memory-Stream zu serialisieren und dann zu deserialisieren, sobald Sie zum UI-Thread zurückgekehrt sind.

0voto

vcsjones Punkte 134300

Wie Bob Vale richtig feststellt; es ist eine Faustregel, UI-Objekte niemals auf einem anderen Thread zu erstellen. Wenn Sie Präsentationsobjekte erstellen, sollten Sie dies auf dem UI-Thread tun. Die Hintergrundaufgabe sollte einfache Daten zurückgeben. Ich würde das DoWork so ändern:

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        // Nehmen wir an, hier wird irgendeine Art von "Arbeit" erledigt.
        e.Result = "Einige fettgedruckte Text im Absatz";
    }

Dann könnten Sie den Dokumentinhalt über den Dispatcher setzen:

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Action 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 diesem Fall ermöglicht Ihnen der Dispatcher, Arbeit (einen Delegaten in diesem Fall) gegen den Thread zu planen, der das UI besitzt.

0voto

Scott Weinstein Punkte 18520

Ich habe ein 'FlowDocument', ein WPF-Objekt, aus dem DoWork des BackgroundWorker-Objekts von System.ComponentModel erstellt.

Machen Sie das nicht. UI-Objekte müssen vom UI-Thread erstellt und aktualisiert werden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X