我正在创建一个WPF MVVM应用程序。我有一个漫长的过程,我想在另一个线程中运行,同时向用户显示忙碌指示器。我遇到的问题如下:
BusyIndicator控件的IsBusy属性绑定到我的视图模型的IsBusy公共属性,该属性实现了INotifyPropertyChanged接口。如果我使用Join运行下面的代码,那么用户界面不会显示忙指示符,因为主UI线程正在等待线程“t”完成。如果我删除了连接,那么托管WPF的Windows窗体会过早关闭。我知道跨线程访问Windows窗体是一个很大的问题,但是我想要做的就是关闭Form I,最简单的解决方案是将_hostForm.Close()移动到“DoLongProcess”方法的末尾。当然,如果我这样做,我会得到一个交叉线程异常。您能否建议采取最佳方法来应对这种情况?
<extToolkit:BusyIndicator IsBusy="{Binding Path=IsBusy}" >
<!-- Some controls here -->
</extToolkit:BusyIndicator>
private void DoSomethingInteresting() {
// Set the IsBusy property to true which fires the
// notify property changed event
IsBusy = true;
// Do something that takes a long time
Thread t = new Thread(DoLongProcess);
t.Start();
t.Join();
// We're done. Close the Windows Form
IsBusy = false;
_hostForm.Close();
}
答案 0 :(得分:8)
在这种情况下,最好的办法是在您实际调用表单关闭之前,通知您要关闭的所有系统,这将使您有机会在最后运行任何进程。当您完成并想要从另一个线程关闭表单时,您需要使用以下命令在UI线程上调用它:
_hostForm.BeginInvoke(new Action(() => _hostForm.Close()));
如果您可能总是从另一个线程关闭表单,实际创建close方法的线程安全版本可能会更好;即:
public class MyForm : Form
{
// ...
public void SafeClose()
{
// Make sure we're running on the UI thread
if (this.InvokeRequired)
{
BeginInvoke(new Action(SafeClose));
return;
}
// Close the form now that we're running on the UI thread
Close();
}
// ...
}
使用这种方法,您可以在运行异步操作时继续更新表单及其UI,然后在完成后调用关闭和清理。
答案 1 :(得分:3)
我建议您使用BackgroundWorker课程。请遵循以下示例:
BackgroundWorker wrk = new BackgroundWorker();
wrk.WorkerReportsProgress = true;
wrk.DoWork += (a, b) =>
{
... your complex stuff here
};
wrk.RunWorkerCompleted += (s, e) =>
{
isBusy=false;
_hostForm.Close();
};
wrk.RunWorkerAsync();
RunWorker complete中的代码已经在UI线程上。此解决方案是非阻塞的,因此您可以看到isBusy已更改且UI正常响应。 DoWork部分在另一个线程中执行,但如果需要,您也可以使用ReportProgress功能。
答案 2 :(得分:2)
这是我对你的建议。我将使用{4}中包含的TPL中的Tasks
来解决这个问题:
private void DoSomethingInteresting()
{
IsBusy = true;
var task = new Task(() => DoLongProcess());
task.ContinueWith(previousTask =>
{
IsBusy = false;
_hostForm.Close();
}, TaskScheduler.FromCurrentSynchronizationContext());
task.Start();
}
修改强>
说明:工作在后台完成一项任务。完成此任务后,第二个任务.ContinueWith...
将自动启动,并且由于TaskScheduler.FromCurrentSynchronizationContext()
而在UI线程上运行。