我有一个带有statusLabel的Windows窗体(C#.NET),我似乎无法在事件处理程序方法的进程中间进行更新。我的代码看起来像这样…
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方法。由于某种原因,我无法使状态标签显示“进程已开始”。它仅显示“处理已完成!”。如您所见,我尝试使包含状态标签但未成功的状态条无效,刷新和更新。我无法在statuslabel本身上调用update / refresh / invalidate,因为这些方法不可用。我究竟做错了什么?
添加的信息:
通过单击表单上的按钮来启动“过程”,该表单在一个单独的类中调用方法,如下所示:
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方法,但没有帮助。我仍然得到相同的结果。这是我的更新方法。
Application.DoEvents()
updateStatusLabel
private void updateStatusLabel(string text) { StatusLabel1.Text = text; statusStrip1.Refresh(); Application.DoEvents(); }
因此,我猜想“处理”是在UI线程上进行的,但是eventhandler在它自己的线程上被调用,然后事件处理程序又在UI线程上调用控件更新。这是一种愚蠢的做事方式吗?注意:包含DoSomeProcess()方法的类位于我正在引用的单独的.NET ClassLibrary中。
如果您正在UI线程上进行 处理 ,则在处理运行时它将无法执行其他任何操作(例如重新绘制更新的标签)。因此,例如,如果处理是由于用户单击按钮而发生的,并且是由按钮单击处理程序触发的(没有将其显式放置在另一个线程上),则该处理在UI线程上运行。即使您更新了标签的文本,也不会在收到画图消息之前将其绘制出来,此时,它可能正在忙于处理。
答案是在单独的线程上进行长时间运行的处理。hack(IMHO)Application.DoEvents用于使UI线程在处理过程中执行一些UI任务。如果在更新标签后且开始处理之前放入其中之一,则标签被重新粉刷的几率非常高。但是,在处理过程中,再也无法处理任何绘画事件(当有人将另一个应用程序窗口移到您的应用程序上并向后移动时,将导致半绘制窗口)。因此,我称其为hack(尽管,呃,嗯,我已经知道这样做了:-))。
Application.DoEvents
*根据您的修改进行 *编辑 更新:
回覆
因此,我猜想“处理”正在UI线程上进行,但是eventhandler在它自己的线程上被调用…
我假设DoSomeProcess是从UI线程触发的(例如,直接响应按钮单击或类似操作)。如果是这样,那么可以,您的处理肯定在UI线程上。因为TriggerProcessStarted您是通过 异步 触发您的回调的BeginInvoke,所以您不知道何时运行该回调,但是无论如何您的代码都会立即启动处理,而不会屈服,因此没有其他人能够抓住该线程。由于这是UI线程,因此对委托的Invoke调用将在设置标签文本的调用中阻止,此后它必须等待UI线程(正在忙于处理)。(并且这是假设它安排在不同的线程上;我无法100%相信自己的任何一种方式,因为微软有两种不同的方式BeginInvokes-IIRC的一位设计师已经承认这是一个非常愚蠢的想法- 自从我与这些东西战斗以来已经有一段时间了。)
DoSomeProcess
TriggerProcessStarted
BeginInvoke
Invoke
如果您TriggerProcessStarted同步进行对回调的调用,则可以。但是,理想情况下,应在自己的线程上安排处理(如果不执行UI)。