我有一个执行外部程序来处理媒体文件的WPF应用程序,因此在处理媒体文件时GUI不会冻结,我通过后台工作人员在一个单独的线程上执行该过程。
private void BackgroundWorkerExecProcess(Process process)
{
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = false;
worker.DoWork += DoWork;
worker.RunWorkerCompleted += WorkerCompleted;
worker.RunWorkerAsync(process);
}
void DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
Process process = e.Argument as Process;
process.Start();
string stderr = process.StandardError.ReadToEnd();
//I want to display stderr on main thread
process.WaitForExit();
}
void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//some code to update gui telling user that process has finished
}
所以,如果有东西打印到stderr,我可以在调试器中看到它,但是如果我尝试用字符串stderr做任何事情,例如我有一个名为“_tbLog”的文本框并且
_tbLog.Text+=stderr;
我从编译器得到一个关于它们在不同线程上的错误。有没有办法将对象从工作线程传递给主线程?
答案 0 :(得分:8)
在DoWork中,将e.Result设置为您的对象。在WorkerCompleted中,您可以将该对象取回...它将再次成为类型对象的e.Result。只需将它投射到它的对象上即可。 WorkerCompleted应该在正确的帖子上。
这是我的一个:
private void workerUpdateBuildHistory_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
UpdateStatusModel model = (UpdateStatusModel)e.Argument;
BuildService buildService = new BuildService(model.TFSUrl);
e.Result = buildService.UpdateBuildHistoryList(model);
}
private void workerUpdateBuildHistory_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
BuildHistoryListModel model = (BuildHistoryListModel)e.Result;
if (model != null)
{
listViewPastBuilds.Items.Clear();
foreach (var item in model.Builds)
{
listViewPastBuilds.Items.Add(item);
}
}
}
答案 1 :(得分:3)
使用您的WorkerCompleted事件处理程序对UI进行更改,它在正确的线程上运行。您所要做的就是将字符串传递给事件处理程序。这是DoWorkEventArgs.Result的目的。您将从e.Result的事件处理程序中检索它。因此:
void DoWork(object sender, DoWorkEventArgs e)
{
//...
e.Result = stderr;
}
void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null) DisplayError(e.Error);
else _tbLog.Text += (string)e.Result;
}
答案 2 :(得分:2)
首先,您需要在DoWorkEventArgs.Result属性中放置任何结果对象(在此示例中为字符串列表),然后通过RunWorkerCompletedArgs.Result属性检索此对象
然后,挂钩后台工作程序的事件处理程序RunWorkedCompleted事件,并让它在RunWorkerCompletedEventArgs.Result属性中传回所需的任何对象。
示例:
void DoWork(object sender, DoWorkEventArgs arg)
{
List<string> results = new List<string>();
results.Add("one");
results.Add("two");
results.Add("three");
arg.Results = results;
}
void WorkComplete(object sender, runWorkerCompelteEventArgs arg)
{
//Get our result back as a list of strings
List<string> results = (List<string>)arg.Result;
PrintResults(results);
}
注意:我没有测试过这段代码,但我相信它应该编译。 http://msdn.microsoft.com/en-us/library/system.componentmodel.runworkercompletedeventargs.result.aspx http://msdn.microsoft.com/en-us/library/system.componentmodel.doworkeventargs.aspx
答案 3 :(得分:0)
您也可以使用调度程序@Zembi提及:
this.Dispatcher.Invoke( new Action( () => {
_tbLog.Text+=stderr;
} ) );
你也可以使用TPL来确保在正确的线程上运行
-edit -
以下是关于进行ui更新的不同方法的a good article,包括使用TPL