WPF RichTextBox Создание потока Проблема с потоками - PullRequest
5 голосов
/ 07 апреля 2011

У меня есть небольшая проблема с RTB и генерацией документов в отношении потоков.

Когда в RTB возникает событие TextChanged, создается новая thead, и генерация документа выгружается в это. Это может занять пару секунд с блокировкой вызовов, поэтому он действительно должен быть в другом потоке, чтобы пользовательский интерфейс реагировал.

Проблема, с которой я столкнулся, является исключением, когда я пытаюсь добавить вновь сгенерированный документ в свойство Document RTB. ( Вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им.) Это не из-за того, что вы забыли использовать Dispatcher.Invoke, поскольку именно там генерируется исключение, а потому, что я создаю FlowDocument / Paragraph / Запускать экземпляры в потоке, отличном от потока пользовательского интерфейса (я думаю ??).

Есть ли способ добиться того, что я здесь ищу?

Обновление

    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, я не могу обновить свой RTB в потоке пользовательского интерфейса, создав объект в другом потоке. Существует очень простое решение, которое требует минимального изменения кода, чтобы обойти это, хотя, и я публикую его, чтобы избавить будущих людей от хлопот. Вкратце, в другом потоке создается граф объектов, который затем преобразуется в XAML. Затем поток пользовательского интерфейса преобразует этот 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 ]

3 голосов
/ 08 апреля 2011

Я думаю, что проблема в том, что FlowDocument - это DependencyObject, который не замораживается и поэтому не может быть создан в одном потоке, а затем использован в другом. Я думаю, потому что, когда FlowDocument создается в другом потоке, он имеет другой диспетчер, чем RTB.

Есть способ обойти это здесь .

0 голосов
/ 08 июля 2014

Я искал решение той же проблемы и в итоге нашел альтернативное решение.

Хитрость заключается в следующем:

  1. Создание FlowDocument и всех внутренних элементов нафоновый поток
  2. Используя отражение, измените Диспетчер FlowDocument и все внутренние элементы, возможно, включая стили и ресурсы, для диспетчера пользовательского интерфейса.
  3. Используйте FlowDocument в пользовательском интерфейсе

Решение сохраняет всю работу в фоновом потоке и не включает в себя сериализацию фоновых потоков и десериализацию потоков пользовательского интерфейса, что еще больше улучшит скорость отклика.

Пожалуйста, найдите код в моем блоге .

[Редактировать] Очень нужное примечание об этом решении: это в основном взлом, и еще предстоит решить проблему обработки изображений в FlowDocument, так как изображения должны обрабатываться в потоке переднего плана (UI), который кажетсябыть ограничением самого .Net.

Для проекта, над которым я работал, требовалосьПервую генерацию отчетов Я решил максимально обработать в фоновом потоке и пожертвовать некоторой отзывчивостью GUI на время создания FlowDocument (около 20% от общего времени подготовки отчета).

0 голосов
/ 08 апреля 2011

попробуйте это:

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