Можете ли вы передать объект пользовательского интерфейса, созданный в потоке BackgroundWorker, в основной поток? - PullRequest
0 голосов
/ 24 марта 2019

У меня есть окно wpf с элементом управления RichTextBox.Когда я открываю текстовый файл, я анализирую содержимое и создаю объекты Paragraph, а затем вставляю их в RTB-документ.Все работало нормальноОднако при рефакторинге приложения я решил попытаться перенести эту процедуру в BackgroundWorker.Но когда я пытаюсь назначить полученный FlowDocument из фонового процесса обратно в RTB в основном потоке, я получаю ошибку: процесс не может получить доступ к объекту, потому что он принадлежит другому потоку.Я обращаюсь к FlowDocument из метода Worker RunWorkerCompleted.Должен ли я быть в состоянии передать его в RTB оттуда?

Я попытался установить e.Result для FlowDocument, созданного в методе DoWork, а затем назначить его для RTB в методе RunWorkerCompleted.Я также попытался передать 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;}
}

... и передал это objecto методу:

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.

1 Ответ

0 голосов
/ 24 марта 2019

Вы не можете создать FlowDocumnet в любом другом потоке, кроме потока пользовательского интерфейса. FlowDocument происходит от DispatcherObject.

Только поток, в котором была создана Dispatcher, может получить доступ к DispatcherObject напрямую. Чтобы получить доступ к DispatcherObject из потока кроме потока, в котором был создан DispatcherObject, вызовите Invoke или BeginInvoke на Dispatcher связан DispatcherObject с ...

Объекты, полученные из DispatcherObject, имеют сходство с потоками.

Таким образом, все DispatcherObject, которые будут использоваться в потоке пользовательского интерфейса, должны быть созданы именно в этом потоке, что связано с тем, что модель потоков STA основана на WPF. Единственным исключением является объект, производный от Freezable. Freezable может быть создан в любом потоке и после замораживания передан между потоками, поскольку замораживание приведет к отсоединению Freezable от системы диспетчеризации. Сходство с потоком затем отменяется, и поэтому механизм уведомления Dispatcher больше не будет активен. Вот почему типы Freezable улучшают производительность в замороженном состоянии. Таким образом, чтобы создать FlowDocument в другом потоке, этот поток должен быть потоком пользовательского интерфейса ( Запуск приложения WPF с несколькими потоками пользовательского интерфейса ). Но из-за его привязки к потоку вы можете использовать его только в этом потоке и не можете передать его обратно в другой поток пользовательского интерфейса. Чтобы решить вашу проблему, не запуская секунду Window, вы должны последовать совету Ханса Пассанта, чтобы сериализовать FlowDocument в XDocument, а затем изменить его. Если вы решите создать документ без фонового потока, вы можете отложить его создание с помощью Dispatcher и поиграть с приоритетом (например, DispatcherPriority.Background или DispatcherPriority.Idle), если процедура замедляет пользовательский интерфейс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...