首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF 跨執行緒存取 UI 控制項:Dispatcher.CheckAccess 與 BeginInvoke 完整指南

     編輯:關於.NET

問題由來

WPF 控制項繼承自 DispatcherObject,不再有 WinForms 的 InvokeRequired 與 Invoke/BeginInvoke。直接在背景執行緒存取 UI 屬性將拋出:

「呼叫執行緒無法存取這個物件,因為不同執行緒擁有它。」

WPF 解決方案:Dispatcher 模型

WinFormsWPF說明
Control.InvokeRequiredDispatcherObject.CheckAccess()回傳 bool,true 表示同執行緒可直接存取
Control.VerifyAccess()DispatcherObject.VerifyAccess()非同執行緒拋出例外,用於防禦性程式設計
Control.Invoke/BeginInvokeDispatcher.BeginInvoke/Invoke將委派排入 UI 執行緒 Dispatcher 隊列

標準模式程式碼

private void TryToUpdateButton(object uiObject)
{
    Button btn = uiObject as Button;
    if (btn == null) return;

    // 檢查是否為 UI 執行緒
    if (btn.CheckAccess())
    {
        UpdateButtonUI(btn); // 同執行緒,直接更新
    }
    else
    {
        // 非 UI 執行緒,排入 Dispatcher
        btn.Dispatcher.BeginInvoke(
            DispatcherPriority.Normal,
            new Action<Button>(UpdateButtonUI),
            btn);
    }
}

private void UpdateButtonUI(Button btn)
{
    btn.Content = "Updated at " + DateTime.Now.ToLongTimeString();
    btn.Background = Brushes.LightGreen;
}

進階用法

1. 傳回值的同步呼叫

string result = (string)btn.Dispatcher.Invoke(
    DispatcherPriority.Normal,
    new Func<Button, string>(GetButtonText),
    btn);

2. 任務平行程式庫 (TPL) 整合 (.NET 4.5+)

await btn.Dispatcher.InvokeAsync(() => {
    btn.Content = "Async updated";
});

3. DispatcherPriority 優先級選擇

  • DispatcherPriority.Send:最高,立即處理 (慎用)
  • DispatcherPriority.Normal:預設,適合大多數 UI 更新
  • DispatcherPriority.Background:低優先級,適合非關鍵重繪

常見錯誤與最佳實踐

  1. 勿在背景執行緒建立 UI 物件:即使透過 Dispatcher 建立,視覺樹父子關係仍屬建立執行緒,後續操作仍會跨執行緒
  2. 避免巢狀 BeginInvoke:導致執行順序不可預期,改用 async/await 或 Task 串聯
  3. 長時間運算拆分:使用 DispatcherPriority.Background 分批處理,保持 UI 回應
  4. 記憶體洩漏防範:Lambda 捕獲 UI 物件導致無法釋放,必要時使用 WeakReference
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved