为什么不能控制更新/刷新中间过程
我有一个窗体(C#.net)与一个statusLabel,我似乎无法得到在事件处理程序方法中的进程中更新。 我的代码看起来像这样…
void Process_Completed(object sender, EventArgs e) { string t = "Process is finished!"; this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t }); } void Process_Started(object sender, EventArgs e) { string t = "Process has begun"; this.Invoke(new StatusLabelUpdator(updateStatusLabel), new object[] { t }); } private delegate void StatusLabelUpdator(string text); private void updateStatusLabel(string text) { StatusLabel1.Text = text; statusStrip1.Invalidate(); statusStrip1.Refresh(); statusStrip1.Update(); }
当我运行代码时,一旦进程启动,Process_Started方法被触发,几秒钟后Process_Completed方法被触发。 出于某种原因,我无法获得状态标签以显示“过程已经开始”。 它只显示“过程完成!”。 正如你可以看到我已经尝试无效,刷新和更新状态栏,其中包含状态标签,但没有成功。 我不能在状态标签本身上调用update / refresh / invalidate,因为这些方法不可用。 我究竟做错了什么?
添加信息:
“过程”是通过在单独的类中调用方法的表单上的button单击来启动的,如下所示:
public void DoSomeProcess() { TriggerProcessStarted(); System.Threading.Thread.Sleep(2000); // For testing.. TriggerProcessComplete(); }
而在TriggerProcessxxxx方法中,我用这个代码触发事件…
var EventListeners = EH.GetInvocationList(); //EH is the appropriate EventHandler if (EventListeners != null) { for (int index = 0; index < EventListeners.Count(); index++) { var methodToInvoke = (EventHandler)EventListeners[index]; methodToInvoke.BeginInvoke(this, EventArgs.Empty, EndAsyncEvent, new object[] { }); } }
最后,我已经将Application.DoEvents()
添加到updateStatusLabel
方法,但它没有帮助。 我仍然得到相同的结果。 这是我的更新方法。
private void updateStatusLabel(string text) { StatusLabel1.Text = text; statusStrip1.Refresh(); Application.DoEvents(); }
所以我猜“处理”是在UI线程上发生的,但事件处理程序是在它自己的线程上调用的,然后在UI线程上调用控件更新。 这是一个愚蠢的做事方式? 注意:包含DoSomeProcess()方法的类位于我引用的单独的.NET ClassLibrary中。
如果您正在UI线程上进行处理 ,则在处理运行时,将无法执行其他任何操作(如重新绘制更新后的标签)。 因此,例如,如果由于用户单击button而发生了处理,并且由button单击处理程序触发(未明确将其放置在另一个线程上),则会在UI线程上运行。 即使您更新了标签的文本,它也不会被绘制,直到它收到一个绘制消息,此时可能正忙于处理。
答案是在一个单独的线程上进行长时间运行的处理。 黑客(恕我直言)是使用Application.DoEvents
让UI线程在处理过程中做一些UI的东西。 如果您在更新标签之后并且在开始处理之前添加了其中的一个,那么赔率非常高,标签将被重新绘制。 但是,在处理过程中,不会再有任何绘画事件得到处理(当有人将另一个应用程序窗口移动到应用程序后面时,会导致半绘制的窗口,等等)。 因此,我把它称为黑客(即使,呃,嗯,我已经知道这样做:-))。
根据您的修改编辑更新:
回覆
所以我猜“处理”是在UI线程上发生的,但事件处理程序是在它自己的线程上调用的。
我假设DoSomeProcess
从UI线程触发(例如,直接响应button点击或类似)。 如果是这样,那么是的,你的处理肯定是在UI线程上。 因为TriggerProcessStarted
通过BeginInvoke
asynchronous触发你的callback,所以你不知道什么时候会运行,但是无论如何你的代码会立即开始处理,永远不会屈服,所以没有其他人能够抓住那个线程。 由于这是UI线程,调用委托会阻止Invoke
调用设置标签的文本,于是它必须等待UI线程(正在忙于处理)。 (这是假设它是在不同的线程计划;我不能100%说服自己,因为微软有两个不同的BeginInvoke
– 这个devise师已经承认是一个真正的愚蠢的想法 – 这是一个IIRC而自从我与这个东西战斗。)
如果您将TriggerProcessStarted
调用同步到您的callback,您应该没问题。 但是理想情况下,应该在自己的线程上安排处理(如果不是在UI上)。