您可以将在BackgroundWorker线程中创建的UI对象传递给主线程吗?

时间:2019-03-24 15:42:19

标签: c# wpf

我有一个带有RichTextBox控件的wpf窗口。打开文本文件时,我将解析内容并创建Paragraph对象,然后将其插入RTB文档中。一切正常。但是,在重构应用程序时,我决定尝试将该例程移至BackgroundWorker。但是,当我尝试从后台进程将生成的FlowDocument分配回主线程中的RTB时,我收到一个错误:该进程无法访问该对象,因为它属于另一个线程。 我正在从Worker RunWorkerCompleted方法访问FlowDocument。我应该不能从那里将其附加到实时出价?

我尝试将e.Result设置为在DoWork方法中创建的FlowDocument,然后将其分配给RunWorkerCompleted方法中的RTB。 我还尝试将在主线程中创建的FlowDocument传递给DoWork方法,以防万一我因为在后台线程中创建FlowDocument而遇到错误。

这是我的第一次尝试:

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    string[] paragraphs = (string[])e.Argument;

    FlowDocument document = new FlowDocument();

    foreach (string paragraph in paragraphs)
    {
        // Create paragraph object.
        Paragraph paragraphContent = new Paragraph(new Run(paragraph));
        // Check maximum length.
        paragraphContent.Background = CheckParagraphLength(paragraphContent);
        //Add paragraph to document.
        document.Blocks.Add(paragraphContent);
    }

    e.Result = document;
}

我尝试创建一个对象以传递给DoWork方法:

public class DocInfo
{
    public string[] paragraphs {get; set;}
    public FlowDocument document {get; set;}
}

...并将该对象传递给方法:

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    DocInfo doc = (DocInfo)e.Argument;

    foreach (string paragraph in doc.paragraphs)
    {
        // Create paragraph object.
        Paragraph paragraphContent = new Paragraph(new Run(paragraph));
        // Check maximum length.
        paragraphContent.Background = CheckParagraphLength(paragraphContent);
        // Add paragraph to document.
        doc.document.Blocks.Add(paragraphContent);
    }

    e.Result = doc;
}

首先,RunWorkerCompleted方法是:

private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    Editor.Document = (FlowDocument)e.Result;
}

第二个:

private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    Editor.Document = ((FlowDocument)e.Result).document;
}

在两种情况下,结果都是在RunWorkerCompleted方法中访问对象时出错。

1 个答案:

答案 0 :(得分:0)

除UI线程外,您无法在任何其他线程中创建FlowDocumnetFlowDocument源自DispatcherObject

  

只有创建Dispatcher的线程可以访问   直接DispatcherObject。从线程访问DispatcherObject   除了创建DispatcherObject的线程以外,调用Invoke   或BeginInvoke上的Dispatcher上的DispatcherObject已关联   与...

     

DispatcherObject派生的对象具有线程相似性。

因此,必须在此线程上创建将在UI线程上使用的任何DispatcherObject,这是因为WPF建立在STA线程模型上。唯一的例外是从Freezable派生的对象。可以在任何线程上创建Freezable,并且一旦冻结将其跨线程传递,这是因为冻结会导致Freezable从调度程序系统中脱钩。然后解除线程亲和力,因此Dispatcher通知机制将不再处于活动状态。这就是Freezable类型在冻结状态下提高性能的原因。因此,要在另一个线程中创建FlowDocument,该线程必须是UI线程(Running WPF Application with Multiple UI Threads)。但是由于其线程亲和性,您只能在该线程上使用它,而不能将其传递回另一个UI线程。要在不运行第二个Window的情况下解决问题,您必须遵循Hans Passant的建议,将FlowDocument序列化为XDocument,然后对其进行修改。 如果您决定在不使用后台线程的情况下创建文档,则可以使用Dispatcher推迟创建,并优先使用优先级(例如DispatcherPriority.BackgroundDispatcherPriority.Idle),如果该过程会使UI变慢。