【问题标题】:How do I make cross-threaded calls to a ToolStripStatusLabel?如何对 ToolStripStatusLabel 进行跨线程调用?
【发布时间】:2010-11-10 19:24:30
【问题描述】:

我倾向于在我的大多数应用程序底部使用 StatusStrip 来进行简单的状态更新,偶尔还会使用进度条。

但是,ToolStripStatusLabels 似乎没有从控件继承,因此它们没有 .Invoke 或 .InvokeRequired。那么我将如何进行线程安全调用来更改它的 text 属性呢?

为后代和其他搜索者提供编码答案:

Action<string> test=(text) =>
            {
                if (this._statusStrip.InvokeRequired) this._statusStrip.Invoke(
               new MethodInvoker(() => this._lblStatus.Text = text));
                else this._lblStatus.Text = text;
            };

private void TestInvoker(string text)
    {
        if (this._statusStrip.InvokeRequired) 
            this._statusStrip.Invoke(
                   new MethodInvoker(() => this._lblStatus.Text = text));
        else this._lblStatus.Text = text;
    }

【问题讨论】:

  • @Maslow:在 Control.Invoke 周围的逻辑要小心。文档中列出的规则很微妙,如果不仔细阅读,很容易出错。请检查我在答案中的链接中的代码,以便更妥善地使用 Invoke。例如,您的实现未验证 ToolStrip 是否未释放,也未验证 ToolStrip 的句柄是否已创建。
  • 除了尚未创建句柄之外,看起来这些情况除了抛出异常之外什么都不做,对吧?如果还没有创建句柄,我不确定假装什么都没发生而不抛出异常是否有用。

标签: .net multithreading toolstrip


【解决方案1】:

这是个好问题!

虽然ToolStripStatusLabel 不从控件继承,但包含ToolStrip 可以!使用包含 ToolStrip 的 Invoke 对 ToolStripStatusLabel 进行调用。

这是因为 ToolStrip 手动处理其组件位的绘制,这与 WPF 管理其组件位的 所有 绘制的方式非常相似,而无需为每个位生成单独的句柄。这很有用,因为很容易忘记每个Control 都有一个关联的HANDLE,并且系统只有有限数量的要分发,例如..

(我之前也遇到过这个问题。例如,我在another question 的侧边栏中提到了它。我应该更新该文本以反映我最近的理解。)

【讨论】:

  • 是的,您甚至可以使用在同一线程上创建的 Forms Invoke 方法。但我会选择 Greg D 的解决方案,因为它更合乎逻辑。
  • @sindre,不一定,但可能......而 ToolStripStatusLabels 必须在与 StatusStrip 相同的线程上创建
【解决方案2】:

此外,一般情况下,您不必必须在代码中操作的完全相同的控件上使用 InvokeRequired 和 BeginInvoke,只要您能保证您正在操作的控件是在同一个线程上创建的(例如,在表单的初始化例程中),作为您调用 InvokeRequired /BeginInvoke 的 UI 元素。

【讨论】:

    【解决方案3】:

    您可以通过使用 delegate 关键字和 Control.Invoke() 方法来做到这一点。此示例说明如何管理线程安全的 .Text.ForeColor 调整。

    private delegate void SetToolStripDelegate(string text, Color color);
    
    private void SetToolStrip(string text, Color color)
    {
        statusBar.Text = text;
        statusBar.ForeColor = color;
    }
    

    在线程上下文中,您可以像这样对该方法进行线程安全调用:

    { // thread begin...
    
        // somewhere inside the thread
        Invoke(new SetToolStripDelegate(SetToolStrip), "Connected.", Color.Green);
    
    } // thread end...
    

    【讨论】:

      【解决方案4】:

      简单来说,在传递 StatusStrip Items 的同时,将 StatusStrip 也传递到参数中,并用作 StatusStrip.BeginInvoke 或 Invoke 方法,并将 Status Strip 项目放置在其中。

      下面的代码应该可以帮助您如何调用和更新 StatusStrip,不仅可以从其他任务/线程,还可以从其他类。

      //Coded by Chandraprakash [2017-07-18]
      //frozenprakash.com
      
      using System;
      using System.Threading;
      using System.Threading.Tasks;
      using System.Windows.Forms;
      
      namespace UIUpdateFromOtherClass
      {
      
      public partial class Form1 : Form
      {
          public Form1()
          {
              InitializeComponent();
          }
      
          private void button1_Click(object sender, EventArgs e)
          {
              FN_Execute();
          }
      
          async void FN_Execute()
          {
              Second s = new Second();
              await Task.Run(() => s.Execute(lbl1,
                                              pb1,
                                              ss1,
                                              ss1Lbl1,
                                              ss1Pb1)
                              );
              MessageBox.Show("End");
          }
      
      }
      
      public class Second
      {
          public void Execute(Label lbl1,
                              ProgressBar pb1,
      
                              StatusStrip ss1,
                              ToolStripLabel tsLbl1,
                              ToolStripProgressBar tsPb1)
          {
              lbl1.BeginInvoke(new Action(() =>
                  lbl1.Text = "Second"
              ));
      
              pb1.BeginInvoke(new Action(() =>
              {
                  pb1.Style = ProgressBarStyle.Marquee;
                  pb1.MarqueeAnimationSpeed = 10;
              }));
      
              ss1.BeginInvoke(new Action(() =>
              {
                  tsLbl1.Text = "Second";
      
                  tsPb1.Style = ProgressBarStyle.Marquee;
                  tsPb1.MarqueeAnimationSpeed = 10;
              }));
      
              Thread.Sleep(3000);
          }
      }
      
      }
      

      Windows Forms Screenshot

      【讨论】:

        【解决方案5】:

        您是否尝试过 GetCurrentParent()?这对我有用!

        private delegate void SetToolStripStatusLabelTextDelegate(ToolStripStatusLabel label, string text);
        public static void SetToolStripStatusLabelText(ToolStripStatusLabel label, string text)
        {
            if (label.GetCurrentParent().InvokeRequired)
            {
                label.GetCurrentParent().Invoke(new SetToolStripStatusLabelTextDelegate(SetToolStripStatusLabelText), label, text);
            }
            else
            {
                label.Text = text;
                label.Invalidate();
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-04-17
          • 1970-01-01
          • 2019-05-26
          • 2011-02-24
          • 1970-01-01
          • 2016-08-15
          • 2016-06-16
          相关资源
          最近更新 更多