C# Winform中Invoke的用法總結


 

作者:CrazyEditor   來源:CrazyEditor.cn  
 
 
 
 

Windows 窗體中的控件被綁定到特定的線程,不具備線程安全性 。因此,如果從另一個線程調用控件的方法,那么必須使用控件的一個 Invoke 方法來將調用封送到適當的線程。

在設計中為了讓界面與邏輯分離,我的做法是使用事件,界面只要響應事件來處理界面的顯示就行了。而事件在邏輯處理中可能由不同的線程引發,這些事件的響應方法在修改界面中的控件內容時便會引發一個異常。這時就用到了Control.InvokeRequired 屬性 與Invoke方法。
如果當前線程不是創建控件的線程時,InvokeRequired的值為true;否則為false。換句話說,如果InvokeRequired==true表示其它線程需要訪問控件,那么調用invoke來轉給控件owner處理。

實例一:
首先定義一個委托,如:

 private delegate void InvokeCallback(string msg);

然后判斷這個屬性的值,來決定是否要調用Invoke函數:

 void MessageEvent(string msg)
 {
   if (txtBox1.InvokeRequired)
     {
        InvokeCallback  msgCallback = new InvokeCallback(MessageEvent);
        txtBox1.Invoke(msgCallback, new object [] { msg } );
     } 
  else 
    {
        txtBox1.Text = msg;
     } 
 }

說明:這個函數就是事件處理函數,txtBox1是一個文本框。這樣就做到了窗體中控件的線程安全性。

 

實例二:
public void ButtonOnClick(object sender,EventArgs e)

{

    this.Invoke(new Action(()=>

    {

        button.Text="關閉";

    }));

}
 

以上寫法往往充斥着WinForm構建的程序。

在微軟新一代的界面開發技術WPF中,由於界面呈現和業務邏輯原生態地分開在兩個線程中,所以控件的事件響應函數就不必Invoke了。但是,如果手動開辟一個新線程,那么在這個新線程中改變控件的外觀,則還是要Invoke的。

 

使用SynchronizationContext的Post/Send方法更新主線程UI

SynchronizationContext類在System.Threading命令空間下,可提供不帶同步的自由線程上下文,其中Post方法簽名如下:

public virtual void Post(SendOrPostCallback d,Object state)    //將異步消息調度到一個同步上下文

可以看出我們要異步更新UI控件,第一是要獲取UI線程的上下文了,第二就是調用post方法了,代碼實現:
實例三:
 

  1. SynchronizationContext syncContext = null;  
  2. //窗體構造函數     
  3. public Form1()  
  4. {  
  5.     InitializeComponent();  
  6.       //獲取UI線程同步上下文  
  7.     syncContext = SynchronizationContext.Current;  
  8. }  
  9. private void button1_Click(object sender, EventArgs e)  
  10. {  
  11.     Thread demoThread =new Thread(new ThreadStart(threadMethod));  
  12.     demoThread.IsBackground = true;  
  13.     demoThread.Start();//啟動線程  
  14. }  
  15. private void threadMethod()  
  16. {  
  17.      syncContext.Post(SetLabelText, "修改后的文本");//子線程中通過UI線程上下文更新UI  
  18. }  
  19. private void SetLabelText(object text)  
  20. {  
  21.     this.lable1.Text = text.ToString();  
其他invoke使用語法:namespace  Interface
{
     // 對主線程的setFrameViewImage進行委托
     delegate void deleteFrameView(IntPtr pFrame);
     class RetriveFrameThread
     {
         private IntPtr pframe;
         private RegisterForm m_Form;
         private deleteFrameView dispFrameView;
         public RetriveFrameThread(Form form)
         {
             pframe = IntPtr.Zero;
             m_Form = form  as RegisterForm;
             if (form !=  null )
             {
                 dispFrameView =  new deleteFrameView(m_Form.setFrameViewImage);
             }     
         }
         public void run()
         {
             while ( true )
             {
                 if (CameraDll.retriveFrame( ref pframe) == 0)
                 {
                     //dispFrameView(pframe);               直接使用delegate
                     m_Form.Invoke(dispFrameView, pframe);  使用窗體的invoke
                 }
                 Thread.Sleep(10);
             }
         }
     }
}


免責聲明!

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



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