[英]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
创建上,呼叫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
和如果该过程减慢 UI,则使用优先级(例如DispatcherPriority.Background
或DispatcherPriority.Idle)
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.