由於 Windows 窗體控件本質上不是線程安全的。因此如果有兩個或多個線程適度操作某一控件的狀態(set value),則可能會迫使該控件進入一種不一致的狀態。還可能出現其他與線程相關的 bug,包括爭用和死鎖的情況。於是在調試器中運行應用程序時,如果創建某控件的線程之外的其他線程試圖調用該控件,則調試器會引發一個 InvalidOperationException
本文用一個很簡單的示例來講解這個問題(在窗體上放一個TextBox和一個Button,點擊Button後,在新建的線程中設置TextBox的值)
解決辦法一: 關閉該異常檢測的方式來避免異常的出現
經過測試發現此種方法雖然避免了異常的拋出,但是並不能保證程序運行結果的正確性 (比如多個線程同時設置TextBox1的Text時,很難預計最終TextBox1的Text是什麼)
1

using System;
2

using System.Collections.Generic;
3

using System.ComponentModel;
4

using System.Data;
5

using System.Drawing;
6

using System.Text;
7

using System.Windows.Forms;
8

using System.Threading;
9

10

namespace winformTest
11



{
12

public partial class Form1 : Form
13


{
14

public Form1()
15


{
16

InitializeComponent();
17

Control.CheckForIllegalCrossThreadCalls = false;//這一行是關鍵
18

}
19
20

21

private void button1_Click(object sender, EventArgs e)
22


{
23

SetTextBoxValue();
24

}
25

26

void SetTextBoxValue()
27


{
28

TextBoxSetValue tbsv = new TextBoxSetValue(this.textBox1, "Method1");
29

ThreadStart TS = new ThreadStart(tbsv.SetText);
30

Thread T = new Thread(TS);
31

T.Start();
32

}
33

34

35

class TextBoxSetValue
36


{
37

private TextBox _TextBox ;
38

private string _Value;
39

40

public TextBoxSetValue(TextBox TxtBox, String Value)
41


{
42

_TextBox = TxtBox;
43

_Value = Value;
44

}
45

46

public void SetText()
47


{
48

_TextBox.Text = _Value;
49

}
50

}
51

}
52

}
解決辦法二:通過委托安全調用 1

using System;
2

using System.Collections.Generic;
3

using System.ComponentModel;
4

using System.Data;
5

using System.Drawing;
6

using System.Text;
7

using System.Windows.Forms;
8

9

namespace winformTest
10

{
11

public partial class Form2 : Form
12


{
13

public Form2()
14


{
15

InitializeComponent();
16

}
17
18

19

private void button1_Click(object sender, EventArgs e)
20


{
21

SetTextBoxValue();
22

}
23

24
25

private delegate void CallSetTextValue();
26

//通過委托調用
27

void SetTextBoxValue()
28


{
29

TextBoxSetValue tbsv = new TextBoxSetValue(this.textBox1, "Method2");
30

if (tbsv.TextBox.InvokeRequired)
31


{
32

CallSetTextValue call = new CallSetTextValue(tbsv.SetText);
33

tbsv.TextBox.Invoke(call);
34

}
35

else
36


{
37

tbsv.SetText();
38

}
39

}
40

41

42 class TextBoxSetValue
43


{
44

private TextBox _TextBox;
45

private string _Value;
46

47

public TextBoxSetValue(TextBox TxtBox, String Value)
48


{
49

_TextBox = TxtBox;
50

_Value = Value;
51

}
52

53

public void SetText()
54


{
55

_TextBox.Text = _Value;
56

}
57

58

59


public TextBox TextBox

{
60


set

{ _TextBox = value; }
61


get

{ return _TextBox; }
62

}
63

}
64

}
65

}
第三解決辦法:
利用BackgroundWorker控件 1

using System;
2

using System.Collections.Generic;
3

using System.ComponentModel;
4

using System.Data;
5

using System.Drawing;
6

using System.Text;
7

using System.Windows.Forms;
8

using System.Threading;
9

10

namespace winformTest
11


{
12

public partial class Form3 : Form
13


{
14

public Form3()
15


{
16

InitializeComponent();
17

}
18

19

private void button1_Click(object sender, EventArgs e)
20


{
21

using (BackgroundWorker bw = new BackgroundWorker())
22


{
23

bw.RunWorkerCompleted += SetTextBoxValue;
24

bw.RunWorkerAsync();
25

}
26

}
27
28

void SetTextBoxValue(object sender, RunWorkerCompletedEventArgs e)
29


{
30

TextBoxSetValue tbsv = new TextBoxSetValue(this.textBox1, "Method3");
31

tbsv.SetText();
32

}
33

34

35

class TextBoxSetValue
36


{
37

private TextBox _TextBox;
38

private string _Value;
39

40

public TextBoxSetValue(TextBox TxtBox, String Value)
41


{
42

_TextBox = TxtBox;
43

_Value = Value;
44

}
45

46

public void SetText()
47


{
48

_TextBox.Text = _Value;
49

}
50

}
51

52

}
53

}