Windows Mobile / WinCE 開發常見需求:網路連線管理 (WiFi/GPRS/3G) 由 Worker Thread 負責,連線狀態變更時需通知 UI Thread 更新介面。直接在 Worker Thread 操作 UI 控制項會拋出 Cross-thread operation not valid 例外,因 UI 元素非執行緒安全。
Invoke 將更新動作排程到 UI 執行緒public class ConnectionMgr
{
// 單例模式 (簡化示範)
private static readonly ConnectionMgr _instance = new ConnectionMgr();
public static ConnectionMgr Instance => _instance;
// 事件委派定義
public delegate void MessageEventHandler(string message);
public event MessageEventHandler MessageEvent;
// 觸發事件的保護方法 (執行緒安全)
private void OnMessage(string message)
{
// 取得事件副本避免競態條件
MessageEventHandler handler = MessageEvent;
handler?.Invoke(message);
}
// 模擬連線狀態變更
private void ConnectHandler() => OnMessage("Connected");
private void DisconnectHandler() => OnMessage("Disconnected");
// 對外公開的控制方法
public void SimulateConnect() => ConnectHandler();
public void SimulateDisconnect() => DisconnectHandler();
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// 建構函式訂閱事件
ConnectionMgr.Instance.MessageEvent += OnMessageReceived;
}
// 事件處理常式 (在 Worker Thread 執行緒觸發)
private void OnMessageReceived(string message)
{
// 必須透過 Invoke 切換到 UI 執行緒
if (this.InvokeRequired)
{
this.Invoke(new Action(UpdateMessage), message);
}
else
{
UpdateMessage(message);
}
}
// 實際更新 UI 的方法 (執行於 UI 執行緒)
private void UpdateMessage(string message)
{
eventText.Text = message; // eventText 為 Label 或 TextBox
}
// 測試按鈕
private void btnTestConnect_Click(object sender, EventArgs e)
{
ConnectionMgr.Instance.SimulateConnect();
}
private void btnTestDisconnect_Click(object sender, EventArgs e)
{
ConnectionMgr.Instance.SimulateDisconnect();
}
}
| 技術點 | 說明 |
|---|---|
InvokeRequired | 檢查呼叫端是否為 UI 執行緒;若為 true 需使用 Invoke/BeginInvoke |
Invoke(delegate, args) | 同步封送:封鎖呼叫端直到 UI 執行緒處理完成 |
BeginInvoke | 非同步封送:立即返回,不阻塞 Worker Thread |
Action | 泛型委派,避免手寫委派型別,支援 Lambda 簡潔語法 |
| 事件副本模式 | var handler = Event; handler?.Invoke(...) 防止多執行緒競態導致 NullReference |
若需傳遞複雜狀態物件,可定義泛型事件:
public class ConnectionStateEventArgs : EventArgs
{
public bool IsConnected { get; set; }
public string NetworkType { get; set; } // WiFi/GPRS/3G
public int SignalStrength { get; set; }
public string ErrorMessage { get; set; }
}
// Worker Thread
public event EventHandler StateChanged;
// UI Thread
private void OnStateChanged(object sender, ConnectionStateEventArgs e)
{
this.Invoke(() => UpdateUI(e));
}
Invoke 並等待,同時 UI Thread 正等待 Worker Thread (如 Join、WaitOne),會發生死鎖。改用 BeginInvoke 或重構同步邏輯。FormClosing 移除事件訂閱,防止記憶體洩漏與存取已釋放控制項。Control.Invoke,行為與完整框架一致。| 方案 | 優點 | 缺點 |
|---|---|---|
| 事件 + Invoke (本文) | 標準、鬆耦合、易理解 | 需手寫 Invoke 判斷 |
| BackgroundWorker | 內建 ProgressChanged/RunWorkerCompleted 自動封送 | CF 版本支援有限、靈活度較低 |
| SynchronizationContext | 更通用、可在非 WinForms 環境使用 | CF 實作不完整 |
| 訊息泵 | 底層控制、效能極致 | 複雜、易出錯、非必要勿用 |
事件訂閱 + Control.Invoke 是 .NET Compact Framework 多執行緒 UI 更新的標準模式。掌握 InvokeRequired 判斷、委派封送、事件副本防競態三大要點,即可構建穩健的背景作業與 UI 互動架構。