Compact Framework / Threading - 在选择选项后,MessageBox显示在其他控件上

时间:2008-08-13 16:55:51

标签: c# winforms multithreading compact-framework

我正在开发一个应用程序,可以从外部服务器上获取并安装一堆更新,并且需要一些线程帮助。用户遵循以下过程:

  • 点击按钮
  • 方法检查更新,返回计数。
  • 如果大于0,则询问用户是否要使用MessageBox.Show()安装。
  • 如果是,它会循环运行并在每次更新的run()方法上调用BeginInvoke()以在后台运行它。
  • 我的更新类包含一些用于更新进度条等的事件。

进度条更新很好,但是MessageBox没有完全从屏幕上清除,因为更新循环在用户单击“是”后立即​​启动(参见下面的屏幕截图)。

  • 在更新循环开始之前,我该怎么做才能使消息框立即消失?
  • 我应该使用Threads而不是BeginInvoke()吗?
  • 我应该在单独的线程上进行初始更新检查并从该线程调用MessageBox.Show()吗?

代码

// 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(); });
}

截图

Windows Mobile Bug

3 个答案:

答案 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

You can get away with not calling EndInvoke when dealing with WinForms without any negative consequences.

  

我所知道的唯一记录的规则例外是在Windows窗体中,您可以正式允许调用Control.BeginInvoke而无需调用Control.EndInvoke。

然而,在处理Begin / End Async模式的所有其他情况下,您应该假设它会泄漏,如您所述。