ToolStripStatusLabel 没有 InvokeRequired 属性的解决办法

简介: ToolStripStatusLabel 没有 InvokeRequired 属性的解决办法当编写多线程程序时,你希望在线程中修改 Form 窗体上的控件的文本等属性,但你会得到一个错误:线程间操作无效: 从不是创建控件“xxx”的线程访问它。

ToolStripStatusLabel 没有 InvokeRequired 属性的解决办法

当编写多线程程序时,你希望在线程中修改 Form 窗体上的控件的文本等属性,

但你会得到一个错误:线程间操作无效: 从不是创建控件“xxx”的线程访问它。

引发了“Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException”类型的异常.


这时有一个解决办法就是使用委托来进行 Invoke 调用,

但是你会发现 ToolStripStatusLabel 没有 InvokeRequired 属性!

这个问题存在的原因是什么呢?

我们看看 Textbox 的继承关系:

public abstract class TextBoxBase : Control

再看看 ToolStripStatusLabel  的继承关系:

public abstract class ToolStripItem : Component, IDropTarget, IComponent, IDisposable
再看看 ToolStripStatusLabel  的容器 StatusStrip 的继承关系:

public class ScrollableControl : Control, IComponent, IDisposable
我们会发现,这是因为ToolStripItem 是一个组件 Component,而不是一个控件 Control。
解决办法:

方法其实也比较简单,尝试在拥有它们的工具条、状态栏(StatusStrip)上调用扩展方法,并调整委托方法。

实例一:

public class MyForm : System.Windows.Forms.Form {
             
    //UI 元素
    private Label lblStatus;
     
    private ProgressBar progressBar1;
 
    //Delegate
    private delegate void MyProgressEventsHandler(
        object sender, MyProgressEvents e);
 
     
     private void UpdateUI(object sender, MyProgressEvents e) {
        lblStatus.Text = e.Msg;
        myProgressControl.Value = e.PercentDone;
    }
 
   //ShowProgress 现在可以记录为可从任何线程调用的公共方法。
    public void ShowProgress(string msg, int percentDone) 
   {
    if(InvokeRequired)
    {
       System.EventArgs e = new MyProgressEvents(msg, percentDone);
           object[] pList = { this, e };
 
           BeginInvoke(new MyProgressEventsHandler(UpdateUI), pList);
    }
    else
        {
        UpdateUI(this, new MyProgressEvents(msg,
            PercentDone));    
        }
        
    }
 
     private void btnStart_Click(object sender, EventArgs e)
     {
      //启动线程
      Thread t = new Thread(new ParameterizedThreadStart(RunsOnWorkerThread));
      t.IsBackground = true;
      t.Start(input);
     }
 
    //线程执行函数
    private void RunsOnWorkerThread() 
    {
        int i = 0;   
        while(...) //loop      
        {  
          DoSomethingSlow();
          ShowProgress("test",i);
          ++i;
        }
    }
}

上面的代码,我们看到什么了?

啥,直接使用 “InvokeRequired”,不用使用“ToolStripStatusLabel.InvokeRequired”这样的格式!

对的,这样就可以了。

当然你也可以使用“StatusStrip.InvokeRequired”这样的形式!

实例二:

上面的例子代码可能不太好理解,我们看看微软官方的例子:

下面的代码示例是一个完整的 Windows 窗体应用程序,它包含一个带有三个按钮和一个文本框的窗体。 第一个按钮演示不安全的跨线程访问,第二个按钮演示使用 Invoke 实现的安全访问,而第三个按钮演示使用 BackgroundWorker 实现的安全访问。

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace CrossThreadDemo
{
	public class Form1 : Form
	{
		// This delegate enables asynchronous calls for setting
		// the text property on a TextBox control.
		delegate void SetTextCallback(string text);

		// This thread is used to demonstrate both thread-safe and
		// unsafe ways to call a Windows Forms control.
		private Thread demoThread = null;

		// This BackgroundWorker is used to demonstrate the 
		// preferred way of performing asynchronous operations.
		private BackgroundWorker backgroundWorker1;

		private TextBox textBox1;
		private Button setTextUnsafeBtn;
		private Button setTextSafeBtn;
		private Button setTextBackgroundWorkerBtn;

		private System.ComponentModel.IContainer components = null;

		public Form1()
		{
			InitializeComponent();
		}

		protected override void Dispose(bool disposing)
		{
			if (disposing && (components != null))
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}

		// This event handler creates a thread that calls a 
		// Windows Forms control in an unsafe way.
		private void setTextUnsafeBtn_Click(
			object sender, 
			EventArgs e)
		{
			this.demoThread = 
				new Thread(new ThreadStart(this.ThreadProcUnsafe));

			this.demoThread.Start();
		}

		// This method is executed on the worker thread and makes
		// an unsafe call on the TextBox control.
		private void ThreadProcUnsafe()
		{
			this.textBox1.Text = "This text was set unsafely.";
		}

		// This event handler creates a thread that calls a 
		// Windows Forms control in a thread-safe way.
		private void setTextSafeBtn_Click(
			object sender, 
			EventArgs e)
		{
			this.demoThread = 
				new Thread(new ThreadStart(this.ThreadProcSafe));

			this.demoThread.Start();
		}

		// This method is executed on the worker thread and makes
		// a thread-safe call on the TextBox control.
		private void ThreadProcSafe()
		{
			this.SetText("This text was set safely.");
		}

		// This method demonstrates a pattern for making thread-safe
		// calls on a Windows Forms control. 
		//
		// If the calling thread is different from the thread that
		// created the TextBox control, this method creates a
		// SetTextCallback and calls itself asynchronously using the
		// Invoke method.
		//
		// If the calling thread is the same as the thread that created
		// the TextBox control, the Text property is set directly. 

		private void SetText(string text)
		{
			// InvokeRequired required compares the thread ID of the
			// calling thread to the thread ID of the creating thread.
			// If these threads are different, it returns true.
			if (this.textBox1.InvokeRequired)
			{	
				SetTextCallback d = new SetTextCallback(SetText);
				this.Invoke(d, new object[] { text });
			}
			else
			{
				this.textBox1.Text = text;
			}
		}

		// This event handler starts the form's 
		// BackgroundWorker by calling RunWorkerAsync.
		//
		// The Text property of the TextBox control is set
		// when the BackgroundWorker raises the RunWorkerCompleted
		// event.
		private void setTextBackgroundWorkerBtn_Click(
			object sender, 
			EventArgs e)
		{
			this.backgroundWorker1.RunWorkerAsync();
		}
		
		// This event handler sets the Text property of the TextBox
		// control. It is called on the thread that created the 
		// TextBox control, so the call is thread-safe.
		//
		// BackgroundWorker is the preferred way to perform asynchronous
		// operations.

		private void backgroundWorker1_RunWorkerCompleted(
			object sender, 
			RunWorkerCompletedEventArgs e)
		{
			this.textBox1.Text = 
				"This text was set safely by BackgroundWorker.";
		}

		#region Windows Form Designer generated code

		private void InitializeComponent()
		{
			this.textBox1 = new System.Windows.Forms.TextBox();
			this.setTextUnsafeBtn = new System.Windows.Forms.Button();
			this.setTextSafeBtn = new System.Windows.Forms.Button();
			this.setTextBackgroundWorkerBtn = new System.Windows.Forms.Button();
			this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
			this.SuspendLayout();
			// 
			// textBox1
			// 
			this.textBox1.Location = new System.Drawing.Point(12, 12);
			this.textBox1.Name = "textBox1";
			this.textBox1.Size = new System.Drawing.Size(240, 20);
			this.textBox1.TabIndex = 0;
			// 
			// setTextUnsafeBtn
			// 
			this.setTextUnsafeBtn.Location = new System.Drawing.Point(15, 55);
			this.setTextUnsafeBtn.Name = "setTextUnsafeBtn";
			this.setTextUnsafeBtn.TabIndex = 1;
			this.setTextUnsafeBtn.Text = "Unsafe Call";
			this.setTextUnsafeBtn.Click += new System.EventHandler(this.setTextUnsafeBtn_Click);
			// 
			// setTextSafeBtn
			// 
			this.setTextSafeBtn.Location = new System.Drawing.Point(96, 55);
			this.setTextSafeBtn.Name = "setTextSafeBtn";
			this.setTextSafeBtn.TabIndex = 2;
			this.setTextSafeBtn.Text = "Safe Call";
			this.setTextSafeBtn.Click += new System.EventHandler(this.setTextSafeBtn_Click);
			// 
			// setTextBackgroundWorkerBtn
			// 
			this.setTextBackgroundWorkerBtn.Location = new System.Drawing.Point(177, 55);
			this.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn";
			this.setTextBackgroundWorkerBtn.TabIndex = 3;
			this.setTextBackgroundWorkerBtn.Text = "Safe BW Call";
			this.setTextBackgroundWorkerBtn.Click += new System.EventHandler(this.setTextBackgroundWorkerBtn_Click);
			// 
			// backgroundWorker1
			// 
			this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
			// 
			// Form1
			// 
			this.ClientSize = new System.Drawing.Size(268, 96);
			this.Controls.Add(this.setTextBackgroundWorkerBtn);
			this.Controls.Add(this.setTextSafeBtn);
			this.Controls.Add(this.setTextUnsafeBtn);
			this.Controls.Add(this.textBox1);
			this.Name = "Form1";
			this.Text = "Form1";
			this.ResumeLayout(false);
			this.PerformLayout();

		}

		#endregion


		[STAThread]
		static void Main()
		{
			Application.EnableVisualStyles();
			Application.Run(new Form1());
		}

	}
}


相关参考:

如何:对 Windows 窗体控件进行线程安全调用

InvokeRequired and ToolStripStatusLabel

目录
相关文章
|
4月前
|
JavaScript 前端开发
判断对象是否含有改属性,三个方法
JavaScript中判断对象是否包含属性的三种方法:1. 使用`'property' in object`检查自有属性和继承属性;2. 使用`object.hasOwnProperty('property')`仅检查自有属性;3. 使用`if (object.property)`判断,但返回属性值。
46 2
判断对象是否含有改属性,三个方法
|
XML 数据格式
使用@ContextConfiguration注解后,提示找不到配置文件
使用@ContextConfiguration注解后,提示找不到配置文件
914 0
使用@ContextConfiguration注解后,提示找不到配置文件
|
8月前
|
Android开发
setOnItemClickListener不起作用解决方法
setOnItemClickListener不起作用解决方法
172 2
|
8月前
|
计算机视觉
vs2017修改从父级或项目默认设置继承项
vs2017修改从父级或项目默认设置继承项
188 0
|
Java Android开发
Eclipse中输入点号(.)不提示类成员(函数、字段)的解决办法
Eclipse中输入点号(.)不提示类成员(函数、字段)的解决办法
181 0
Eclipse中输入点号(.)不提示类成员(函数、字段)的解决办法
X11/XWindow更改属性代码
X11/XWindow更改属性代码
122 0
|
C#
解决办法:为什么我的DLL中加载后找不到指定的函数
解决办法:为什么我的DLL中加载后找不到指定的函数
175 0
|
前端开发
vcharts设置legend属性无效解决办法
内容概览 需求: 折线图上有 一个产品的目标值和实际值 ,通过点击其中一个legend,同时隐藏该产品的实际值和目标值(指折线图上的线) 当我觉得这个需求很 easy 的时候,居然发现 动态设置 legend 属性无效 …… 通过本文来记录这个解决过程,前端真是太难了 哈哈 在 v-charts 中, 折线图的写法如下: <ve-line :extend="chartExtend" :data="chartData" :events="chartEvents" height="58vh" ></ve-line>。 而 v-charts 是对 echart 的包装,很多事件都可以从 ech
1408 0
|
前端开发 JavaScript
前端 安装 css peek 按F12 提示找不到定义解决办法
前端 安装 css peek 按F12 提示找不到定义解决办法
1402 0
|
开发工具 git
找不到.gitconfig文件具体位置时的解决方法
找不到.gitconfig文件具体位置时的解决方法
641 0