繁体   English   中英

能否将在 BackgroundWorker 线程中创建的 UI 对象传递给主线程?

[英]Can you pass an UI object created in a BackgroundWorker thread to the main thread?

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

我尝试将 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 方法中的对象时出错。

您不能在 UI 线程以外的任何其他线程中创建FlowDocumnet FlowDocument派生自DispatcherObject

只有创建Dispatcher的线程可以直接访问DispatcherObject 要访问DispatcherObject距离比线程的线程上DispatcherObject创建上,呼叫InvokeBeginInvokeDispatcherDispatcherObject相关联...

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和如果该过程减慢 UI,则使用优先级(例如DispatcherPriority.BackgroundDispatcherPriority.Idle)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM