跨线程调用控件

跨线程调用控件,修改控件的属性,比如有一个text控件,用来显示消息

需要使用begininvoke

 

 

网上随便找了一个代码,应该是靠谱的,需要使用委托,判断控件是否invokeRequired

用委托,具体代码如下~:
public delegate void MyInvoke(string str);

private void button9_Click(object sender, EventArgs e)
        {
            Thread t = new Thread(new ThreadStart(fun));
            t.Start();
        }

        private void fun()
        {
            //_myInvoke("dddd");
            SetText("ddd");
        }
        private void SetText(string s)
        {
            if (textBox6.InvokeRequired)
            {
                MyInvoke _myInvoke = new MyInvoke(SetText);
                this.Invoke(_myInvoke, new object[] { s });
            }
            else
            {
                this.textBox6.Text = s;
            }
        }



最近发现的牛逼方法
txtbox.Invoke((MethodInvoker)delegate()
{
//在这里写处理方法就可以了
});
逐步 txtbox.Invoke();
txtbox.Invoke((MethodInvoker)delegate(){});
delegate后面的()可以去掉

#region 程序集 System.Windows.Forms.dll, v4.0.30319
// C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.Windows.Forms.dll
#endregion

using System;

namespace System.Windows.Forms
{
    // 摘要:
    //     表示一个委托,该委托可执行托管代码中声明为 void 且不接受任何参数的任何方法。
    public delegate void MethodInvoker();
}

 

MethodInvoker 提供一个简单委托,该委托用于调用含 void 参数列表的方法。

在对控件的 Invoke 方法进行调用时或需要一个简单委托又不想自己定义时可以使用该委托。

 

 

下面的代码示例演示如何使用 MethodInvoker 以调用更新应用程序窗体的标题栏的方法。

 

public partial class Form1 : Form
{
    public Form1()
    {
        // Create a timer that will call the ShowTime method every second.
        var timer = new System.Threading.Timer(ShowTime, null, 0, 1000);           
    }

    private void ShowTime(object x)
    {
        // Don't do anything if the form's handle hasn't been created 
        // or the form has been disposed.
        if (!this.IsHandleCreated && !this.IsDisposed)

                return;

        // Invoke an anonymous method on the thread of the form.
        this.Invoke((MethodInvoker) delegate
        {
            // Show the current time in the form's title bar.
            this.Text = DateTime.Now.ToLongTimeString();
        });
    }
}

 

英文提示 2022-06-06

System.InvalidOperationException
  HResult=0x80131509
  Message=Cross-thread operation not valid: Control 'MainForm' accessed from a thread other than the thread it was created on.
  Source=System.Windows.Forms
  StackTrace:
   at System.Windows.Forms.Control.get_Handle()
   at System.Windows.Forms.Control.set_WindowText(String value)
   at System.Windows.Forms.Form.set_WindowText(String value)
   at System.Windows.Forms.Control.set_Text(String value)
   at System.Windows.Forms.Form.set_Text(String value)
   at LisaEncry.MainForm.ATimer_Elapsed(Object sender, ElapsedEventArgs e) in C:\workspace\Company\UK\UKEncryption\SourceCode\LisaEncry\MainForm.cs:line 35
   at System.Timers.Timer.MyTimerCallback(Object state)

  This exception was originally thrown at this call stack:
    [External Code]
    LisaEncry.MainForm.ATimer_Elapsed(object, System.Timers.ElapsedEventArgs) in MainForm.cs
    [External Code]

 

Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on [duplicate]

回答1

The data received in your serialPort1_DataReceived method is coming from another thread context than the UI thread, and that's the reason you see this error.
To remedy this, you will have to use a dispatcher as descibed in the MSDN article:
How to: Make Thread-Safe Calls to Windows Forms Controls

So instead of setting the text property directly in the serialport1_DataReceived method, use this pattern:

delegate void SetTextCallback(string text);

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;
  }
}

So in your case:

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
  txt += serialPort1.ReadExisting().ToString();
  SetText(txt.ToString());
}

 

回答2

I don't know if this is good enough but I made a static ThreadHelperClass class and implemented it as following .Now I can easily set text property of various controls without much coding .

public static class ThreadHelperClass
{
    delegate void SetTextCallback(Form f, Control ctrl, string text);
    /// <summary>
    /// Set text property of various controls
    /// </summary>
    /// <param name="form">The calling form</param>
    /// <param name="ctrl"></param>
    /// <param name="text"></param>
    public static void SetText(Form form, Control ctrl, 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 (ctrl.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(SetText);
            form.Invoke(d, new object[] { form, ctrl, text });
        }
        else
        {
            ctrl.Text = text;
        }
    }
}

Using the code:

 private void btnTestThread_Click(object sender, EventArgs e)
 {
    Thread demoThread =
       new Thread(new ThreadStart(this.ThreadProcSafe));
            demoThread.Start();
 }

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

 

posted @ 2012-11-27 17:54  ChuckLu  阅读(421)  评论(0编辑  收藏  举报