2011-04-07 72 views
4

我在RTB和文档生成方面遇到了一些与线程有关的问题。WPF RichTextBox文档创建线程问题

当RTB上触发TextChanged事件时,将创建一个新的thead,并将文档生成转移到此处。这可能需要几秒钟,阻止调用,所以它确实需要在另一个线程上以保持UI的响应。

我遇到的问题是一个例外,当我尝试将新生成的文档添加到RTB的Document属性。 (调用线程不能访问此对象,因为不同的线程拥有它。)这不是因为忘记使用Dispatcher.Invoke,因为那里生成了异常,而是因为我正在创建FlowDocument/Paragraph/Run实例一个除UI线程以外的线程(我认为??)。

有没有办法实现我在这里找的东西?

更新

private void rtbQuery_TextChanged(object sender, TextChangedEventArgs e) 
    { 
     System.Diagnostics.Debug.WriteLine("Requires update; on thread:" + System.Threading.Thread.CurrentThread.ManagedThreadId); 

     backgroundWorker.RunWorkerAsync(); 
    } 

    private void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 
    { 
     System.Diagnostics.Debug.WriteLine("Generating; on thread:" + System.Threading.Thread.CurrentThread.ManagedThreadId); 

     DocumentGenerator dgen = new DocumentGenerator(); 
     string queryText = getQueryText(); 

     e.Result = dgen.GenerateDocument(queryText); 
    } 

    private void backgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) 
    { 
     System.Diagnostics.Debug.WriteLine("Assigning; on thread:" + System.Threading.Thread.CurrentThread.ManagedThreadId); 

     FlowDocument doc = (FlowDocument)e.Result; 
     txtQuery.Document = doc; // ! The calling thread cannot access this object because a different thread owns it 
    } 

>Requires update; on thread:9 
>Generating; on thread:10 
>Assigning; on thread:9 

更新#2 - 一种解决方案

(的种类)

所以,@乔恩·米切尔指出的那样,我不能在UI线程上更新我的RTB,并在上创建一个对象其他线程。有一个非常简单的解决方案,即只需最少的代码更改,即可解决此问题,并且我将它发布以免未来人员麻烦。简而言之,在另一个线程上创建一个对象图,然后转换为XAML。然后,UI线程将该XAML转换回对象图形,并在其自己的线程中运行,并且一切正常。

private void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 
    { 
     DocumentGenerator dgen = new DocumentGenerator(); 
     string queryText = getQueryText(); 

     dgen.GenerateDocument(queryText); // start generation 
     e.Result = dgen; // note, i'm passing the generator, not the document 
    } 

    private void backgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) 
    { 
     DocumentGenerator dgen = (DocumentGenerator)e.Result; 
     txtQuery.Document = dgen.GetFlowDocument(); 
    } 

在DocumentGenerator类

public void GenerateDocument(string data) 
    { 
     ... // build up the document DOM 

     // return documentDOM; // used to return the generated item here. 
     documentXAML = System.Windows.Markup.XamlWriter.Save(documentDOM); // serialize the DOM to XAML 
    } 
    public FlowDocument GetDocument() 
    { 
     object result = System.Windows.Markup.XamlReader.Parse(documentXAML); // build DOM from XAML 
     return (FlowDocument)result; 
    } 

回答

3

我认为这个问题是因为FlowDocumentDependencyObject这是不冻结的,因此不能在一个线程被创建,然后在不同的人用。我认为它的原因是当FlowDocument在其他线程上创建时,它具有不同的调度程序,即RTB。

这是一个解决这个here

+0

ahhh ....这就是链接。这是一个痛苦,但我会很满意XAML方法。感谢所有的帮助。辛苦赚来的积分;) – jasper 2011-04-08 11:15:18

0

试试这个:

private void backgroundWorker_RunWorkerCompleted(object sender, 
     System.ComponentModel.RunWorkerCompletedEventArgs e)  
    {   
     System.Diagnostics.Debug.WriteLine(
      "Assigning; on thread:" + 
      System.Threading.Thread.CurrentThread.ManagedThreadId); 

     Dispatcher.BeginInvoke(new Action(
      delegate() 
      { 
       FlowDocument doc = (FlowDocument)e.Result; 
       txtQuery.Document = doc; 
      } 
     ), null); 
    } 
+0

与上面相同的错误。如上所述,它不是一个Dispatcher问题,Jon似乎已经查明了这个问题(查看他的链接) – jasper 2011-04-08 11:19:07

0

我一直在寻找一个解决同样的问题,并最终想出了一个替代解决方案。

诀窍是:

  1. 创建的FlowDocument和在后台线程
  2. 所有内部元件使用反射改变的FlowDocument的调度和所有内部元件可能包括样式和资源,UI调度。
  3. 使用的FlowDocument在UI

该解决方案保持在后台线程所有的工作,不涉及后台线程序列化和UI线程反序列化,这将进一步提高响应速度。

请在my blog post找到代码。

[编辑]关于此解决方案的一个非常需要的注意事项:它基本上是一种黑客攻击,并且尚未解决处理FlowDocument中图像的问题,因为图像需要在前景(UI)线程上处理,似乎是.Net本身的限制。

对于我所研究过的项目,我决定在后台线程上尽可能多地处理前台报表生成,并在构建FlowDocument的时候牺牲一些图形用户界面的响应时间(大约占总报表准备时间的20%)。