WinForm 子線程修改主線程(UI線程)Control 【Z】


原文http://www.cnblogs.com/SkySoot/archive/2012/03/14/2396552.html

我們先來看一段運行時會拋出 InvalidOperationException 異常的代碼段:

public partial class TestThread : Form
{
    public TestThread()
    {
        InitializeComponent();
    }
 
    Thread thread;
 
    void SetText(string str)
    {
        this.textBox1.Text = str;
    }
 
    void DoSomething()
    {
        // do......
        SetText("XXX");
    }
 
    private void buttonX1_Click(object sender, EventArgs e)
    {
        thread = new Thread(new ThreadStart(DoSomething));
        thread.IsBackground = true;
        thread.Start();
    }
}

image

在VS2005或者更高版本中,只要不是在控件的創建線程(一般就是指UI主線程)上訪問控件的屬性就會拋出這個錯誤,解決方法就是利用控件提供的Invoke和BeginInvoke把調用封送回UI線程,也就是讓控件屬性修改在UI線程上執行.

下面給出修改后正確調用的代碼:

比較直接的修改方法像這樣:

public partial class TestThread : Form
{
    delegate void ChangeTextBoxValue(string str); // 新增委托代理
 
    public TestThread()
    {
        InitializeComponent();
    }
 
    Thread thread;
 
    void SetText(string str)
    {
        this.textBox1.Text = str;
    }
 
    void DoSomething()
    {
        // do......
        this.BeginInvoke(new ChangeTextBoxValue(SetText),"XXX"); // 也可用 this.Invoke調用
    }
 
    private void buttonX1_Click(object sender, EventArgs e)
    {
        thread = new Thread(new ThreadStart(DoSomething));
        thread.IsBackground = true;
        thread.Start();
    }
}

不過,要考慮到也許程序中不止子線程會調用 DoSomething()方法,也許主線程或其他模塊多處調用這個方法,最漂亮的改法是這樣:

public partial class TestThread : Form
{
    delegate void ChangeTextBoxValue(string str);
 
    public TestThread()
    {
        InitializeComponent();
    }
 
    Thread thread;
 
    void SetText(string str)
    {
        if (this.InvokeRequired) // 獲取一個值指示此次調用是否來自非UI線程
        {
            this.Invoke(new ChangeTextBoxValue(SetText), str);
        }
        else
        {
            this.textBox1.Text = str;
        }            
    }
 
    void DoSomething()
    {
        // do......
        SetText("ABCDEFG");
    }
 
    private void buttonX1_Click(object sender, EventArgs e)
    {
        thread = new Thread(new ThreadStart(DoSomething));
        thread.IsBackground = true;
        thread.Start();
    }
}

this.Invoke(new ChangeTextBoxValue(SetText), str) // 在擁有控件的基礎窗口句柄的線程上,用指定的參數列表執行指定委托

this.BeginInvoke(new ChangeTextBoxValue(SetText), str); // 在創建控件的基礎句柄所在線程上,用指定的參數異步執行指定委托。

無論是同步還是異步,單步跟蹤調試代碼會發現,這些方法還是會回到UI線程執行,其中通過了代理而已.

這兩個方法向UI線程的消息隊列中放入一個消息,當UI線程處理這個消息時,就會在自己的上下文中執行傳入的方法,換句話說凡是使用BeginInvoke和Invoke調用的線程都是在UI主線程中執行的,所以如果這些方法里涉及一些靜態變量,不用考慮加鎖的問題.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM