WPF RichTextBox文档创建线程问题

时间:2011-04-07 10:11:20

标签: .net wpf multithreading richtextbox

关于线程,我在RTB和文档生成方面遇到了一些问题。

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

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

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

更新

    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 - 解决方案

(各种各样)

所以,正如@Jon Mitchell指出的那样,我无法在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 个答案:

答案 0 :(得分:3)

我认为问题是因为FlowDocumentDependencyObject,它不是可冻结的,因此无法在一个线程上创建,然后在另一个线程上使用。我认为这是因为当FlowDocument在另一个线程上创建时,它有一个不同的调度程序,到RTB。

有一个解决方案here

答案 1 :(得分: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);
    }

答案 2 :(得分:0)

我一直在寻找解决同一问题的方案,并最终想出了另一种解决方案。

诀窍是:

  1. 在后台线程上创建FlowDocument和所有内部元素
  2. 使用反射将FlowDocument的Dispatcher和所有内部元素(可能包括样式和资源)更改为UI调度程序。
  3. 在UI中使用FlowDocument
  4. 该解决方案将所有工作保留在后台线程中,不涉及后台线程序列化和UI线程反序列化,这将进一步提高响应能力。

    请在my blog post中找到代码。

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

    对于我工作的项目,我决定在后台线程上尽可能多地处理后台线程,并在构建FlowDocument时牺牲一些GUI响应(大约占总报告准备时间的20%)。