为什么 StatusBar.Invoke 方法不适用于 ToolStripProgressBar?

发布于 2024-12-08 09:40:48 字数 1537 浏览 0 评论 0原文

我最近一直在开发一个应用程序,我想通过 StatusBar 控件中包含的 ToolStripProgressBar 控件在状态栏中显示另一个线程的进度。在尝试添加此代码之前,我最初使用代码更改了 ToolStripStatusLabel 控件的文本,为此,我使用带有委托的 Invoke 方法,一切正常。但是,我发现当我尝试使用 ToolStripProgressBar 执行此操作时,对状态栏的 Invoke 方法的调用失败,并且没有通知(没有错误,没有异常,没有任何通知)。我后来了解到,要以这种方式使用进度条,我必须使用 BackgroundWorker 控件。所以我的代码可以工作,但我不明白为什么我不能使用似乎已经可以工作的 Invoke 方法。

一些有效和无效的示例:

这个有效

 public delegate void changeStatusMessage(String message);
 public changeStatusMessage changeStatusMessageDelegate;
 public void changeStatusMessageMethod(String message){
      if(statusbar.InvokeRequired){
           statusbar.Invoke(changeStatusMessageDelegate, new Object[] {message});
      }else{
           toolstripLabel.Text = message;
      }
 }

这个无效

 public delegate void incrementProgressBar(int value);
 public incrementProgressBar incrementProgressBarDelegate;
 public void incrementProgressBarMethod(int value){
      if(statusbar.InvokeRequired){
           statusbar.Invoke(incrementProgressBarDelegate, new Object[] {value});
      }else{
           toolstripProgress.Increment(value);
      }
 }

在无效的示例中,InvokeRequired 属性为 true,因此调用 Invoke 方法然后什么也没有发生。正如我预期的那样,这次它会再次调用 incrementProgressBarMethod,其中 InvokeRequired 为 false,从而允许触发 Increment 方法。

我真的很想知道为什么这不起作用。正如我所说,我已经重新调整以使用 BackgroundWorker,我只是想要一个解释。

I have recently been working on an application where I wanted to display the progress of another thread in the status bar via the ToolStripProgressBar control that is contained in the StatusBar control. Before I attempted to add this code I originally had the code changing the text of a ToolStripStatusLabel control and to do this I used the Invoke method with delegates and everything worked fine. However, I found that when I attempted this with the ToolStripProgressBar the call to the status bar's Invoke method failed without a notification (no error, no exception, nothing). What I have since learned is that to use a progress bar in this way I had to use a BackgroundWorker control. So my code works but I don't understand why I couldn't use the Invoke method that already seemed to work.

Some examples of what worked and what didn't:

This worked

 public delegate void changeStatusMessage(String message);
 public changeStatusMessage changeStatusMessageDelegate;
 public void changeStatusMessageMethod(String message){
      if(statusbar.InvokeRequired){
           statusbar.Invoke(changeStatusMessageDelegate, new Object[] {message});
      }else{
           toolstripLabel.Text = message;
      }
 }

This did not work

 public delegate void incrementProgressBar(int value);
 public incrementProgressBar incrementProgressBarDelegate;
 public void incrementProgressBarMethod(int value){
      if(statusbar.InvokeRequired){
           statusbar.Invoke(incrementProgressBarDelegate, new Object[] {value});
      }else{
           toolstripProgress.Increment(value);
      }
 }

In the example that didn't work the InvokeRequired property is true so the Invoke method is called and then nothing happens. Where as I expected it to call the incrementProgressBarMethod again this time where InvokeRequired is false and thus allowing the Increment method to fire.

I would really like to know why this doesn't work. As I said I have already retooled to use a BackgroundWorker, I just want an explanation.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

终止放荡 2024-12-15 09:40:48

Invoke 调用 postmessage API 并将消息放入 Windows 消息队列中。如果 UI 线程被阻塞,那么可能会出现死锁,因为它无法推送排队的消息,什么也不会发生。所需调用的另一端不会被触发,如果有东西在等待它,就会发生死锁。

这就是您需要小心 Invoke 的原因。

如何在 .NET 中调用父线程上的函数?

但是您的问题在于创建委托,它是一个空委托,您需要在调用调用它的同一线程上创建委托,因为其他方式,底层系统将无法封送委托(它是一个指针)。

    private void changeStatusMessageMethod(String message)
    {
        if (this.InvokeRequired)
        {
            var changeStatusMessageDelegate = new changeStatusMessage(changeStatusMessageMethod);
            this.Invoke(changeStatusMessageDelegate, new Object[] { message });
        }
        else
        {
            toolstripLabel.Text = message;
        }
    }
    delegate void incrementProgressBar(int value);
    private void incrementProgressBarMethod(int value)
    {
        if (this.InvokeRequired)
        {
            var incrementProgressBarDelegate = new incrementProgressBar(incrementProgressBarMethod);
            this.Invoke(incrementProgressBarDelegate, new Object[] { value });
        }
        else
        {
            toolstripProgress.Increment(value);
        }
    }

这适用于 dotnet 框架 v4

    private void button1_Click(object sender, EventArgs e)
    {
        var t = new System.Threading.Thread(new System.Threading.ThreadStart(x));
        t.Start();
    }

    private void x()
    {
        do
        {
            changeStatusMessageMethod(DateTime.Now.ToString());
            System.Threading.Thread.Sleep(1000);
        } while (true);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        var t = new System.Threading.Thread(new System.Threading.ThreadStart(y));
        t.Start();
    }

    private void y()
    {
        do
        {
            incrementProgressBarMethod(1);
            System.Threading.Thread.Sleep(1000);
        } while (true);
    }

Invoke calls postmessage API and enqueue message on windows message. If UI thread is blocked, then you can have a deadlock, because it cant push queued message, nothing will happens. The other side of invoke required is not fired, and if something is waiting for it, bang, deadlock.

This is the reason you need to be careful with Invoke.

How to invoke a function on parent thread in .NET?

But your problem is on creation of the delegate, it is a null delegate, you need to create the delegate on same thread that it is being called by invoke, because other way, the underling system will fail on marshaling the delegate (its a pointer).

    private void changeStatusMessageMethod(String message)
    {
        if (this.InvokeRequired)
        {
            var changeStatusMessageDelegate = new changeStatusMessage(changeStatusMessageMethod);
            this.Invoke(changeStatusMessageDelegate, new Object[] { message });
        }
        else
        {
            toolstripLabel.Text = message;
        }
    }
    delegate void incrementProgressBar(int value);
    private void incrementProgressBarMethod(int value)
    {
        if (this.InvokeRequired)
        {
            var incrementProgressBarDelegate = new incrementProgressBar(incrementProgressBarMethod);
            this.Invoke(incrementProgressBarDelegate, new Object[] { value });
        }
        else
        {
            toolstripProgress.Increment(value);
        }
    }

This works on dotnet framework v4

    private void button1_Click(object sender, EventArgs e)
    {
        var t = new System.Threading.Thread(new System.Threading.ThreadStart(x));
        t.Start();
    }

    private void x()
    {
        do
        {
            changeStatusMessageMethod(DateTime.Now.ToString());
            System.Threading.Thread.Sleep(1000);
        } while (true);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        var t = new System.Threading.Thread(new System.Threading.ThreadStart(y));
        t.Start();
    }

    private void y()
    {
        do
        {
            incrementProgressBarMethod(1);
            System.Threading.Thread.Sleep(1000);
        } while (true);
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文