我正在开发一个应用程序,可以从外部服务器上获取并安装一堆更新,并且需要一些线程帮助。用户遵循以下过程:
进度条更新很好,但是MessageBox没有完全从屏幕上清除,因为更新循环在用户单击“是”后立即启动(参见下面的屏幕截图)。
代码
// Button clicked event handler code...
DialogResult dlgRes = MessageBox.Show(
string.Format("There are {0} updates available.\n\nInstall these now?",
um2.Updates.Count), "Updates Available",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2
);
if (dlgRes == DialogResult.Yes)
{
ProcessAllUpdates(um2);
}
// Processes a bunch of items in a loop
private void ProcessAllUpdates(UpdateManager2 um2)
{
for (int i = 0; i < um2.Updates.Count; i++)
{
Update2 update = um2.Updates[i];
ProcessSingleUpdate(update);
int percentComplete = Utilities.CalculatePercentCompleted(i, um2.Updates.Count);
UpdateOverallProgress(percentComplete);
}
}
// Process a single update with IAsyncResult
private void ProcessSingleUpdate(Update2 update)
{
update.Action.OnStart += Action_OnStart;
update.Action.OnProgress += Action_OnProgress;
update.Action.OnCompletion += Action_OnCompletion;
//synchronous
//update.Action.Run();
// async
IAsyncResult ar = this.BeginInvoke((MethodInvoker)delegate() { update.Action.Run(); });
}
截图
答案 0 :(得分:6)
您的UI未更新,因为所有工作都在用户界面线程中进行。 您致电:
this.BeginInvoke((MethodInvoker)delegate() {update.Action.Run(); })
是在创建“this”(你的表单)的线程上调用update.Action.Run(),这是用户界面线程。
Application.DoEvents()
确实会给UI线程重绘屏幕的机会,但我很想创建新的委托,并在那上面调用BeginInvoke。
这将在从线程池分配的单独线程上执行update.Action.Run()函数。然后,您可以继续检查IAsyncResult,直到更新完成,在每次检查后查询更新对象的进度(因为您不能让其他线程更新进度条/ UI),然后调用Application.DoEvents()。
之后你也应该调用EndInvoke(),否则你可能会泄漏资源
我也很想在进度对话框上添加一个取消按钮,并添加一个超时,否则如果更新卡住(或需要太长时间),那么你的应用程序将永远锁定。
答案 1 :(得分:1)
你有没有尝试过
Application.DoEvents()
在这里
if (dlgRes == DialogResult.Yes)
{
Application.DoEvents();
ProcessAllUpdates(um2);
}
答案 2 :(得分:1)
@ John Sibly
我所知道的唯一记录的规则例外是在Windows窗体中,您可以正式允许调用Control.BeginInvoke而无需调用Control.EndInvoke。
然而,在处理Begin / End Async模式的所有其他情况下,您应该假设它会泄漏,如您所述。