我正在为程序构建一个UI,我无法弄清楚为什么我的进度条在单击转换按钮后不会显示。
private void convertButton_Click(object sender, EventArgs e)
{
toolStripProgressBar.Visible = true;
...
toolStripProgressBar.Visible = false;
}
我在Python中遇到了与tkinter类似的问题,我不得不调用一个函数来更新空闲任务。有没有办法在不使用线程的情况下使用Windows窗体执行此操作?
编辑:在旁注中,这是toolStrip中的进度条,它还包含一个使用状态栏文本更新的标签。有没有办法让标签在左侧而进度条在另一侧而不是在左边彼此相邻?
答案 0 :(得分:4)
嗯, 是一种不使用线程(Application.DoEvents
)的方法,但我强烈建议不要使用它。 Re-entrancy是令人讨厌的,你真的不希望UI线程被束缚。
使用BackgroundWorker
代替 - 它非常简单,而且进度条几乎是设计的。无需使用单独的线程并将进度报告回UI线程。不需要Control.Invoke
等 - 它会照顾你。
有lots of tutorials for BackgroundWorker
- 你不应该花太多时间来使用它。
答案 1 :(得分:1)
根据你问的方法来解决这个问题,没有线程,就是用Application.DoEvents();来做。 (只需在将进度条设置为可见后立即添加该调用。)
现在我同意Jon Skeet,虽然BackgroundWorker是一种更好的方法,但确实使用了一个单独的线程。
答案 2 :(得分:1)
您需要在与UI线程分开的线程中执行您的进程,然后让它定期向UI线程报告其进度。如果你的转换操作在UI线程内部工作,那么在操作完成之前它将无法响应。
答案 3 :(得分:1)
我猜测问题是代码中的"..."
是一个长时间运行的过程。 UI更新不是即时的,但必须在Windows中通过消息队列运行,然后绘制到屏幕上。抽取队列并在与事件相同的线程中进行绘制。
因此,任何长时间运行的任务都需要移动到不同的线程。更重要的是,您的行代码需要在该线程终止后调用。否则,您设置进度条,然后立即再将其关闭。
一种方法是使用BackgroundWorker控件。
答案 4 :(得分:1)
只有在允许绘制在处理消息期间发生的情况时,才能看到进度条。当您处于事件处理程序的中间时,通常不会发生消息处理。如果要显示进度条,则必须将visiblitity设置为true,启动后台线程以完成工作并从处理程序返回。
答案 5 :(得分:1)
现在,我会尽快解释一下。在windows窗体应用程序中发生的大多数事情发生在一个线程中,通常是同一个线程Main()运行。如果你打开Program.cs,你会看到Main()有一行如下所示:
Application.Run(new Form1());
如果您随时调试应用程序并检查调用堆栈,您将看到它将追溯到该Run方法。这意味着Windows窗体应用程序实际上是Run方法的连续运行。那么,Run在做什么?运行正在进入消息队列,Windows通过该队列向其发送消息。运行然后将这些消息发送到正确的控件,这些控件本身会执行诸如添加与按下的键相对应的文本,重绘自身等等。请注意,所有这些都发生在无限循环和单个线程一起运行时,所以你要输入的天气或者只是移动窗口,这些消息的负载被传递到应用程序,而应用程序又处理它们并相应地做出反应,所有这些都在该单个线程中。控件还可以通过队列向自己发送消息,甚至可以通过Control.BeginInvoke将消息放入泵中。这些控件所做的一件事就是根据发生的事情来提升事件。因此,如果单击一个按钮,您编写的用于处理该单击的代码最终将间接由Application.Run方法运行。
现在,您的代码正在发生的事情是,即使您将进度条的可见状态更改为可见,然后更新其值,您也可以将其可见性更改为false,所有这些都在同一方法中。这意味着只有在离开方法后,Application.Run()才能继续迭代并使用消息队列,有效地要求进度条更新其显示。当发生这种情况时,您已经将进度条的可见性保留为false,这是您在退出方法之前所做的最后一件事。 DoEvents()是一个快速而又脏的解决方法,因为它读取队列中的消息并处理它们。我觉得使用它并不是很舒服,因为它会带来重入问题。
使用线程是一个很好的解决方案,但我建议在这种情况下使用ThreadPool线程而不是自定义线程,因为我倾向于仅在我拥有有限数量的长寿命线程的情况下使用自定义线程我需要控制他们的生命周期。使用线程的最简单和最实用的方法是使用BackgroundWorker组件,即使我建议您了解如何使用代理执行Windows窗体多线程,如果您想真正了解正在发生的事情。
答案 6 :(得分:0)
我的解决方案是在状态条上调用refresh 我相信这会导致UI线程重新绘制状态条。
toolStripStatusBar1.PerformStep();
statusStrip1.Refresh();
这适用于.NET 4.0。虽然这个问题很老,但这是我在谷歌搜索这个问题时发现的第一个问题。