为啥不能控制更新/刷新中间过程
Posted
技术标签:
【中文标题】为啥不能控制更新/刷新中间过程【英文标题】:Why won't control update/refresh mid-process为什么不能控制更新/刷新中间过程 【发布时间】:2011-01-21 10:53:56 【问题描述】:我有一个带有 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 方法。由于某种原因,我无法让状态标签显示“进程已开始”。它只显示“过程已完成!”。如您所见,我已尝试使包含状态标签但未成功的状态条失效、刷新和更新。我不能在状态标签本身上调用更新/刷新/无效,因为这些方法对它不可用。我做错了什么?
添加信息:
“进程”是通过单击窗体上的按钮启动的,该窗体调用一个单独的类中的方法,如下所示:
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[] );
最后,我在updateStatusLabel
方法中添加了Application.DoEvents()
,但它没有帮助。我仍然得到相同的结果。这是我的更新方法。
private void updateStatusLabel(string text)
StatusLabel1.Text = text;
statusStrip1.Refresh();
Application.DoEvents();
所以我猜“处理”发生在 UI 线程上,但事件处理程序在它自己的线程上被调用,然后在 UI 线程上调用控件更新。这是一种愚蠢的做事方式吗?注意:包含 DoSomeProcess() 方法的类位于我引用的单独 .NET ClassLibrary 中。
【问题讨论】:
相关帖子 - Force GUI update from UI Thread & How do I update the GUI from another thread? 【参考方案1】:如果您在 UI 线程上进行处理,则在处理运行时它将无法执行任何其他操作(例如重绘更新的标签)。因此,例如,如果由于用户单击按钮而发生处理并由按钮单击处理程序触发(没有明确地将其放置在另一个线程上),则它在 UI 线程上运行。即使您更新了标签的文本,它也不会被绘制,直到它接收到绘制消息,此时它可能正忙于进行处理。
答案是对separate thread 进行长时间运行的处理。 hack(恕我直言)是使用Application.DoEvents
让 UI 线程在您的处理过程中执行一些 UI 工作。如果您在更新标签之后并开始处理之前放置其中之一,那么标签将被重新绘制的可能性非常高。但是,在处理过程中,无法处理进一步的绘制事件(当有人将另一个应用程序窗口移到您的应用程序上并移回时,导致半绘制窗口等)。因此,我称其为 hack(尽管,呃,嗯,我已经知道这样做了 :-))。
编辑根据您的编辑进行更新:
回复
所以我猜“处理”是在 UI 线程上进行的,但事件处理程序是在它自己的线程上调用的......
我假设DoSomeProcess
是从 UI 线程触发的(例如,直接响应按钮单击或类似操作)。如果是这样,那么是的,您的处理肯定在 UI 线程上。因为TriggerProcessStarted
通过BeginInvoke
触发你的回调异步,你不知道它什么时候运行,但无论如何你的代码会立即启动处理,永远不会屈服,所以没有其他人会去能够抓住那个线程。由于那是 UI 线程,因此对委托的调用将在设置标签文本的 Invoke
调用上阻塞,因此它必须等待 UI 线程(正在忙处理)。 (假设它被安排在不同的线程上;无论哪种方式,我都无法 100% 说服自己,因为微软有两个不同的BeginInvoke
s——IIRC 的一位设计师承认这是一个非常愚蠢的想法——而且它是我已经有一段时间没有和这些东西打架了。)
如果您将TriggerProcessStarted
调用同步到您的回调,则应该没问题。但理想情况下,将处理(如果它不做 UI)安排在它自己的线程上。
【讨论】:
我尝试了您的“hack”,但似乎没有帮助。我现在在上面的问题中添加了更多细节。 编辑后:你是对的。我独自离开了 BeginInvoke,但更改了我的按钮单击事件以在它自己的线程上启动 DoSomeWork。这解决了它!以上是关于为啥不能控制更新/刷新中间过程的主要内容,如果未能解决你的问题,请参考以下文章