为啥不能控制更新/刷新中间过程

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% 说服自己,因为微软有两个不同的BeginInvokes——IIRC 的一位设计师承认这是一个非常愚蠢的想法——而且它是我已经有一段时间没有和这些东西打架了。)

如果您将TriggerProcessStarted 调用同步到您的回调,则应该没问题。但理想情况下,将处理(如果它不做 UI)安排在它自己的线程上。

【讨论】:

我尝试了您的“hack”,但似乎没有帮助。我现在在上面的问题中添加了更多细节。 编辑后:你是对的。我独自离开了 BeginInvoke,但更改了我的按钮单击事件以在它自己的线程上启动 DoSomeWork。这解决了它!

以上是关于为啥不能控制更新/刷新中间过程的主要内容,如果未能解决你的问题,请参考以下文章

vue整个页面不能编辑

自己用C语言构造数据包,实现TCP三次握手过程,为啥中间会产生一个RST信号?

2021-07-08-spring 为啥使用三级代理

《结对-贪吃蛇-开发过程》

为啥我不能在 chrome 控制台中展开这个事件对象?

在 ORM 设计器中,为啥服务器对象不能通过新的更改正确刷新?