這是一個古老的話題。。。直入主題吧
windows窗體應用禁止任意線程直接訪問UI控件,只能是創建控件的線程才能操作控件。
但是程序員又不知道是哪個線程創建了控件。於是微軟提供了InvokeRequired屬性和Invoke()方法來實現跨線程操作控件。
示例程序和代碼:
public partial class UIUpdateDemoForm : Form
{
public UIUpdateDemoForm()
{
InitializeComponent();
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (object? sender, ElapsedEventArgs e) =>
{
Debug.WriteLine("定時器執行線程ID=" + Thread.CurrentThread.ManagedThreadId);
this.appendTextBox(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
};
timer.Start();
button1.Enabled = false;
}
/// <summary>
///
/// </summary>
private void appendTextBox(string text)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() =>
{
appendTextBox(text);
}));
}
else
{
richTextBox1.Text += text + Environment.NewLine;
richTextBox1.SelectionStart = richTextBox1.Text.Length;
richTextBox1.ScrollToCaret();
Debug.WriteLine("操作UI的線程:" + Thread.CurrentThread.ManagedThreadId);
}
}
}
上述代碼中,this.InvokeRequired屬性就是用來判斷當前線程是否爲UI線程:如果是UI線程,則this.InvokeRequired=false。如果不是UI線程,則this.InvokeRequired=true。
而檢測到當前線程不是UI線程時,自然無權操作UI控件。然後就通過this.Invoke()方法,將new Action(() =>{xxxx })內的代碼交給UI線程來執行,即UI線程會繼續執行後面的appendTextBox(text)。
上述示例程序的輸出爲:
定時器執行線程ID=7
操作UI的線程:1
即定時器觸發時是7號線程執行代碼,當執行到更新UI操作是線程已切換爲1號。
另外,查看方法定義可以看到this.Invoke()有多個重載,雖然參數不同但是作用是相同的。