2009-09-04 80 views
5

可能重複:
Getting Cross-thread operation not valid
Cross-thread operation not valid跨線程操作無效

我試圖讓我創造新的處理程序以偵聽COM端口用於SerialPort.DataReceived事件。邏輯很簡單 - 我向TextBox1寫東西,按Button1,我的文本應該在Label1中顯示它自己。但我的應用程序不想運行,因爲它會拋出'跨線程操作無效'錯誤。 我做了一些搜索並找到了Invoke對象 - 我如何在我的例子中使用它?爲什麼我需要包含調用邏輯?

namespace WindowsApplication1 
{ 
public partial class Form1 : Form 
{ 
    SerialPort sp = new SerialPort(); 

    public Form1() 
    { 
     InitializeComponent(); 
     sp.DataReceived += MyDataReceivedHandler; 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 

    } 

    private void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e) 
    { 
     try 
     { 
      //sp.PortName = "COM3"; 
      //sp.Open(); 
      Label1.Text = sp.ReadLine(); 
     } 
     catch (Exception exception) 
     { 
      RichTextBox1.Text = exception.Message + "\n\n" + exception.Data; 
     } 
     finally 
     { 
      sp.Close(); 
     } 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     try 
     { 
      sp.PortName = "COM3"; 
      sp.Open(); 
      sp.WriteLine(TextBox1.Text); 
     } 
     catch (Exception exception) 
     { 
      RichTextBox1.Text = exception.Message + "\n\n" + exception.Data; 
     } 
     finally 
     { 
      sp.Close(); 
     } 
    } 
} 

}

+0

@ Peter:這裏的串口是RS232串口。儘管USB和所有仍然有一些設備(例如GPS,醫療)使用串行端口進行PC通信。 – Sesh 2009-09-04 06:55:49

+0

@_simon_:只是好奇:在這個特定的應用程序中使用的COM端口是什麼? – 2009-09-04 07:31:14

+0

@_simon_:我更新了我的答案 – 2009-09-04 09:13:57

回答

17

我的猜測是,MyDataReceivedHandler是在不同的線程不是GUI運行。爲了解決這個問題,你需要在正確的線程上調用Text設置器。這是這樣的一個樣本:

public void SetControlText(Control control, string text) 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new Action<Control,string>(SetControlText), new object[] { control, text }); 
    } 
    else 
    { 
     control.Text = text; 
    } 
} 

private void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e) 
{ 
    try 
    { 
     //sp.PortName = "COM3"; 
     //sp.Open(); 
     SetControlText(Label1, sp.ReadLine()); 
    } 
    catch (Exception exception) 
    { 
     SetControlText(RichTextBox1, exception.Message + "\n\n" + exception.Data); 
    } 
    finally 
    { 
     sp.Close(); 
    } 
} 

如果您使用的是.NET Framework 2.0中,上述Action<T1, T2>委託是不可用的,所以你必須定義自己的一個:

private delegate void SetControlTextHandler(Control control, string text); 

public void SetControlText(Control control, string text) 
{ 
    if (this.InvokeRequired) 
    { 
     this.Invoke(new SetControlTextHandler(SetControlText), new object[] { control, text }); 
    } 
    else 
    { 
     control.Text = text; 
    } 
} 

SetControlText方法可以這樣做短(甚至靜態)(此工作在兩個2.0和3.5):

public static void SetControlText(Control control, string text) 
{ 
    ´control.Invoke((MethodInvoker)delegate { control.Text = text; }); 
} 

那麼你不需要做檢查,但是您將在另一方面將呼叫包裝在委託中,即使它不是必需的。我認爲在像這樣的GUI方法中,這兩者之間的任何性能差異都是可以忽略的,所以我傾向於使用較短的形式,因爲它的代碼編寫較少。

+0

看起來,這隻適用於3.5。我使用Visual Studio 2005,現在我安裝了3.5 SP1。在Visual Studio 2005中,我可以設置使用哪個.NET框架? – sventevit 2009-09-04 08:07:08

+0

@_simon_:我用2.0兼容版本更新了答案 – 2009-09-04 09:07:22

+0

注意:如果在委託中執行的操作長時間運行,它仍然可以阻止UI,因爲調用只會導致UI線程處理操作。在實現它的所有控件上使用BeginInvoke將以異步方式執行操作,而不會阻塞。 – 2009-09-04 20:38:11

0

您也可以執行以下操作每當從一個不同的線程訪問UI控制比它被上創建的一個:

(.NET 3.5)

myControl.BeginInvoke(new MethodInvoker(() => myControl.whatever = whatever;)); 

或 (.NET 2.0)

myControl.BeginInvoke(new MethodInvoker(delegate { myControl.whatever = whatever;)); 

編輯>有時候使用調用長期運行的操作可/仍將掛UI,使用明顯的BeginInvoke執行該操作異步的d ui不會掛起。