C# 紧凑框架/线程 - 选择选项后消息框显示在其他控件上
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10071/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Compact Framework/Threading - MessageBox displays over other controls after option is chosen
提问by Brian Lyttle
I'm working on an app that grabs and installs a bunch of updates off an an external server, and need some help with threading. The user follows this process:
我正在开发一个应用程序,它从外部服务器获取并安装一堆更新,需要一些线程方面的帮助。用户遵循以下流程:
- Clicks button
- Method checks for updates, count is returned.
- If greater than 0, then ask the user if they want to install using MessageBox.Show().
- If yes, it runs through a loop and call BeginInvoke() on the run() method of each update to run it in the background.
- My update class has some events that are used to update a progress bar etc.
- 点击按钮
- 方法检查更新,返回计数。
- 如果大于 0,则询问用户是否要使用 MessageBox.Show() 进行安装。
- 如果是,它会运行一个循环并在每次更新的 run() 方法上调用 BeginInvoke() 以在后台运行它。
- 我的更新类有一些用于更新进度条等的事件。
The progress bar updates are fine, but the MessageBox is not fully cleared from the screen because the update loop starts right after the user clicks yes (see screenshot below).
进度条更新很好,但 MessageBox 并未从屏幕上完全清除,因为更新循环在用户单击是后立即开始(请参见下面的屏幕截图)。
- What should I do to make the messagebox disappear instantly before the update loop starts?
- Should I be using Threads instead of BeginInvoke()?
- Should I be doing the initial update check on a separate thread and calling MessageBox.Show() from that thread?
- 我应该怎么做才能让消息框在更新循环开始之前立即消失?
- 我应该使用线程而不是 BeginInvoke() 吗?
- 我应该在单独的线程上进行初始更新检查并从该线程调用 MessageBox.Show() 吗?
Code
代码
// 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(); });
}
Screenshot
截屏
采纳答案by John Sibly
Your UI isn't updating because all the work is happening in the user interface thread. Your call to:
您的 UI 没有更新,因为所有工作都在用户界面线程中进行。您致电:
this.BeginInvoke((MethodInvoker)delegate() {update.Action.Run(); })
is saying invoke update.Action.Run() on the thread that created "this" (your form), which is the user interface thread.
是说在创建“this”(您的表单)的线程上调用 update.Action.Run() ,这是用户界面线程。
Application.DoEvents()
will indeed give the UI thread the chance to redraw the screen, but I'd be tempted to create new delegate, and call BeginInvoke on that.
确实会给 UI 线程重绘屏幕的机会,但我很想创建新的委托,并在其上调用 BeginInvoke。
This will execute the update.Action.Run() function on a seperate thread allocated from the thread pool. You can then keep checking the IAsyncResult until the update is complete, querying the update object for its progress after every check (because you can't have the other thread update the progress bar/UI), then calling Application.DoEvents().
这将在从线程池分配的单独线程上执行 update.Action.Run() 函数。然后您可以继续检查 IAsyncResult 直到更新完成,在每次检查后查询更新对象的进度(因为您不能让其他线程更新进度条/UI),然后调用 Application.DoEvents()。
You also are supposed to call EndInvoke() afterwards otherwise you may end up leaking resources
你也应该在之后调用 EndInvoke() 否则你最终可能会泄漏资源
I would also be tempted to put a cancel button on the progress dialog, and add a timeout, otherwise if the update gets stuck (or takes too long) then your application will have locked up forever.
我也很想在进度对话框上放一个取消按钮,并添加一个超时,否则如果更新卡住(或花费太长时间),那么您的应用程序将永远锁定。
回答by JamesSugrue
Have you tried putting a
你有没有试过把
Application.DoEvents()
in here
在这里
if (dlgRes == DialogResult.Yes)
{
Application.DoEvents();
ProcessAllUpdates(um2);
}
回答by Quibblesome
@ John Sibly
@约翰·西伯利
你可以逃脱不使用的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.
我所知道的规则的唯一记录例外是在 Windows 窗体中,您可以在其中正式允许调用 Control.BeginInvoke,而不必费心调用 Control.EndInvoke。
However in all other cases when dealing with the Begin/End Async pattern you should assume it will leak, as you stated.
但是,在处理开始/结束异步模式的所有其他情况下,您应该假设它会泄漏,正如您所说。