winforms後臺線程更新UI控件-InvokeRequired方案

這是一個古老的話題。。。直入主題吧

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()有多個重載,雖然參數不同但是作用是相同的。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章