Ну, у меня есть следующая проблема, и я надеюсь, что вы можете мне помочь:
Я хотел бы создать приложение 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;
}
}