Ну, у меня есть следующая проблема, и я надеюсь, что вы можете мне помочь:
Я хотел бы создать приложение WPF с фоновым рабочим для обновления richtextboxes и других элементов пользовательского интерфейса. Этот фоновый работник должен обрабатывать некоторые данные, например. обрабатывать содержимое папки, выполнять парсинг и многое другое. Поскольку я хотел бы перемещать как можно больше кода вне основного класса, я создал класс под названием MyProcess.cs, как вы можете видеть ниже (на самом деле этот класс не имеет большого смысла до сих пор, он будет заполнен гораздо более если эта проблема решена). Общая функциональность должна быть:
- MainWindow: будет создан массив строк (с именем
this.folderContent) - MainWindow: фоновый работник запускает этот массив в качестве аргумента
- MainWindow: будет вызван метод
DoWork()(я знаю, этот теперь выполняется в новом потоке) - MyProcess: создает (пока неформатированный) абзац на основе заданного массива строк
- MainWindow: если рабочий фон заканчивается, вызывается метод
RunWorkerCompleted()(выполняется в потоке пользовательского интерфейса), который должен обновлять WPF RichTextBox с помощью аргумента return метода
Этот последний шаг вызывает исключение InvalidOperationsException запиской, что "вызывающий поток не может получить доступ к этому объекту, потому что ему принадлежит другой поток". Я немного прочитал о классе рабочего класса и его функциональности. Поэтому я думаю, что это как-то связано с вызовом this.formatedFilenames.Inlines.Add(new Run(...)) в методе Execute() MyProcess. Если заменить атрибут Paragraph на список строк или что-то подобное (без дополнительных вызовов new()), я могу получить доступ к этому элементу без каких-либо проблем методом get. Все примеры, связанные с фоновым работником, которые я нашел, возвращают только базовые типы или простые классы.
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
this.process = new MyProcess();
this.worker = new BackgroundWorker();
this.worker.DoWork += worker_DoWork;
this.worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
this.process.Execute((string[])e.Argument);
e.Result = this.process.Paragraph();
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.rtbFolderContent.Document.Blocks.Clear();
// the next line causes InvalidOperationsException:
// The calling thread cannot access this object because a different thread owns it.
this.rtbFolderContent.Document.Blocks.Add((Paragraph)e.Result);
}
...
// folderContent of type string[]
this.worker.RunWorkerAsync(this.folderContent);
...
Изменить:. Так как это задано: RunWorkerAsync вызывается, например. на событие нажатия кнопки или после того, как папка была выбрана с помощью диалогового окна, поэтому в потоке пользовательского интерфейса.
MyProcess.cs
class MyProcess
{
Paragraph formatedFilenames;
public MyProcess ()
{
this.formatedFilenames = new Paragraph();
}
public void Execute(string[] folderContent)
{
this.formatedFilenames = new Paragraph();
if (folderContent.Length > 0)
{
for (int f = 0; f < folderContent.Length; ++f)
{
this.formatedFilenames.Inlines.Add(new Run(folderContent[f] + Environment.NewLine));
// some dummy waiting time
Thread.Sleep(500);
}
}
}
public Paragraph Paragraph()
{
return this.formatedFilenames;
}
}