我有一个带有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方法中访问对象时出错。
答案 0 :(得分:0)
除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
推迟创建,并优先使用优先级(例如DispatcherPriority.Background
或DispatcherPriority.Idle)
,如果该过程会使UI变慢。