WPF RichTextBox文档创建线程问题



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

当TextChanged事件在RTB上触发时,将创建一个新的头部,并将文档生成卸载到此。这可能需要几秒钟,阻塞调用,所以它真的需要在另一个线程上保持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 -解决方案

(各种各样的)

所以,正如@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;
    }

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

这个问题的解决方法可以在AAron的博客上找到:

FlowDocument doc = new FlowDocument(new Paragraph(new Run("hi")));
System.IO.MemoryStream stream = new System.IO.MemoryStream();
XamlWriter.Save(doc, stream);
stream.Position = 0;
      
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ZeroDelegate)delegate ()
{
    flowDoc = (FlowDocument)XamlReader.Load(stream);
});

try this:

    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);
    }

我也在寻找同样问题的解决方案,最终想到了另一个解决方案。

技巧是:

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

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

请在我的博客文章中找到代码。

[编辑]一个非常需要注意这个解决方案:它基本上是一个hack和一个还没有解决的问题处理图像在FlowDocument图像需要处理的前景(UI)线程,这似乎是一个限制。

对于我所从事的需要前台报表生成的项目,我决定尽可能多地在后台线程上处理,并牺牲一些GUI响应时间来构建FlowDocument(约占总报表准备时间的20%)。

最新更新