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

时间:2020-03-05 18:39:26  来源:igfitidea点击:

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

  • 点击按钮
  • 方法检查更新,返回计数。
  • 如果大于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(); });
}

截屏

解决方案

回答

我们是否尝试过将

Application.DoEvents()

在这里

if (dlgRes == DialogResult.Yes)
{
   Application.DoEvents(); 
   ProcessAllUpdates(um2); 
}

回答

用户界面未更新,因为所有工作都在用户界面线程中进行。
我们打给:

this.BeginInvoke((MethodInvoker)delegate() {update.Action.Run(); })

就是说在创建" this"(表单)(即用户界面线程)的线程上调用update.Action.Run()。

Application.DoEvents()

确实会给UI线程重绘屏幕的机会,但是我很想创建新的委托,并在其上调用BeginInvoke。

这将在从线程池分配的单独线程上执行update.Action.Run()函数。然后,我们可以继续检查IAsyncResult,直到更新完成为止,在每次检查后都要查询更新对象的进度(因为我们不能让另一个线程更新进度条/ UI),然后调用Application.DoEvents()。

我们还应该在此后调用EndInvoke(),否则可能会导致资源泄漏

我也很想在进度对话框上放置一个取消按钮,并添加一个超时,否则,如果更新卡住(或者花费太长时间),则应用程序将永远被锁定。

回答

@约翰·西伯里

我们可以在处理WinForms时不调用EndInvoke,而不会产生任何负面影响。

The only documented exception to the rule that I'm aware of is in Windows Forms, where you are officially allowed to call Control.BeginInvoke without bothering to call Control.EndInvoke.

但是,在所有其他情况下,处理"开始/结束"异步模式时,我们应该假定它会泄漏,如我们所说。