C# Solve System.InvalidOperationException: "Invalid inter-thread operation: Control "..." is accessed from a thread other than the one that created it."

error code

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        Task.Run(() => // 模拟另一线程
        {
    
    
            // 在另一线程内做一些耗时操作
            //...
            string result = "...";
            
            // 输出结果(将引发异常)
            textBox1.Text = result; // System.InvalidOperationException
        });
    }
}

exception information

中文:
System.InvalidOperationException:“线程间操作无效: 从不是创建控件“textBox1”的线程访问它。”

英文:
System.InvalidOperationException: Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.

Solution

Method 1 (not recommended): Disable cross-thread checking

useControl.CheckForIllegalCrossThreadCalls = false;

For example:

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
        Control.CheckForIllegalCrossThreadCalls = false; // 禁用跨线程检查
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        Task.Run(() => // 模拟另一线程
        {
    
    
            // 在另一线程内做一些耗时操作
            //...
            string result = "...";
            
            // 输出结果(不会引发异常)
            textBox1.Text = result;
        });
    }
}

Method 2: Using delegates

Use the or method Controlof the base class to execute code that prohibits cross-threading.InvokeBeginInvoke

For example:

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        Task.Run(() => // 模拟另一线程
        {
    
    
            // 在另一线程内做一些耗时操作
            //...
            string result = "...";
            
            // 输出结果(不会引发异常)
            this.Invoke(() =>
            {
    
    
                textBox1.Text = result;
            });
        });
    }
}

Use parameters to pass:

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        Task.Run(() => // 模拟另一线程
        {
    
    
            // 在另一线程内做一些耗时操作
            //...
            string result = "...";
            
            // 输出结果(不会引发异常)
            this.Invoke((string text, int length) =>
            {
    
    
                textBox1.Text = $"Text={
      
      text}, Length={
      
      length}";
            }, result, result.Length);
        });
    }
}

Writing without lambda expressions:

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        Task.Run(() => // 模拟另一线程
        {
    
    
            // 在另一线程内做一些耗时操作
            //...
            string result = "...";
            
            // 输出结果(不会引发异常)
            this.Invoke(new SetTextEvent(SetText), result, result.Length);
        });
    }

    private void SetText(string text, int length)
    {
    
    
        textBox1.Text = $"Text={
      
      text}, Length={
      
      length}";
    }

    private delegate void SetTextEvent(string text, int length);
}

Using BeginInvokethe method Invokeis similar to using the method. The difference between the two is: Invokethe method will complete the work in the main thread (here refers to the GUI thread); BeginInvokeit will complete the work in a new thread (of course, this will not report an error).

Method 3: Use BackgroundWorker to complete the whole time-consuming process (not only changing the GUI)

For example:

// Form1.cs
public partial class Form1 : Form
{
    
    
    public Form1()
    {
    
    
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
    
    
        using (BackgroundWorker backgroundWorker = new BackgroundWorker())
        {
    
    
            backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
            backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);
            backgroundWorker.RunWorkerAsync(); // 可以传入参数
        }

        // 后续过程(不会被 BackgroundWorker 阻塞,即不会等待其完毕再执行)
        // ...
    }

    private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
    
    
        //e.Argument // 可以取得传入的参数

        // 模拟耗时操作
        //...
        string result = "...";
        
        // 传递结果
        e.Result = result;
    }

    private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    
    
        textBox1.Text = e.Result.ToString(); // 输出结果
    }
}

Another example with progress feedback and support for cancellation:
see my other article: C# BackgroundWorker simple example

References

Guess you like

Origin blog.csdn.net/xzqsr2011/article/details/131454098