聊透多線程編程-線程基礎-3.C# Thread 如何從非UI線程直接更新UI元素

目錄

1. 使用?Control.Invoke?或?Control.BeginInvoke(Windows Forms)

2. 使用?Dispatcher.Invoke?或?Dispatcher.BeginInvoke(WPF)

3. 使用?SynchronizationContext


?

桌面應用程序(如 Windows Forms 或 WPF)中,UI 操作必須由主線程(也稱 UI 線程)執行。如果嘗試從非 UI 線程直接更新 UI 元素,通常會引發異常或導致不可預測的行為。

Thread?類本身無法直接更新 UI,但可以通過以下方法將操作委托給 UI 線程來實現安全的 UI 更新。


1. 使用?Control.Invoke?或?Control.BeginInvoke(Windows Forms)

在 Windows Forms 應用程序中,可以使用 Control.Invoke?或 Control.BeginInvoke?方法將代碼調度到 UI 線程。

示例代碼:

using System;
using System.Reflection.Emit;
using System.Threading;
using System.Windows.Forms;
using static System.Net.Mime.MediaTypeNames;class Program : Form
{private Button button;private Label label;public Program(){button = new Button { Text = "Start Thread", Dock = DockStyle.Top };label = new Label { Text = "Waiting...", Dock = DockStyle.Fill };button.Click += Button_Click;Controls.Add(label);Controls.Add(button);}private void Button_Click(object sender, EventArgs e){Thread thread = new Thread(UpdateLabel);thread.Start();}private void UpdateLabel(){for (int i = 0; i < 10; i++){// 檢查是否需要調用 Invokeif (label.InvokeRequired){// 使用 Invoke 將操作調度到 UI 線程label.Invoke(new Action(() => label.Text = $"Count: {i}"));}else{// 如果當前線程是 UI 線程,則直接更新label.Text = $"Count: {i}";}Thread.Sleep(500); // 模擬工作}}[STAThread]static void Main(){Application.EnableVisualStyles();Application.Run(new Program());}
}

解釋:

  • InvokeRequired:檢查當前線程是否是創建控件的線程(即 UI 線程)。如果不是,則需要通過?Invoke?或?BeginInvoke?調度到 UI 線程。
  • Invoke:同步執行指定的操作,等待操作完成后再繼續。
  • BeginInvoke:異步執行指定的操作,不阻塞當前線程。

?

輸出效果:
點擊按鈕后,label?的文本會每 500 毫秒更新一次,顯示當前計數值。


2. 使用?Dispatcher.Invoke?或?Dispatcher.BeginInvoke(WPF)

在 WPF 應用程序中,可以使用 Dispatcher?對象將操作調度到 UI 線程。

示例代碼:

using System;
using System.Reflection.Metadata;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using static System.Net.Mime.MediaTypeNames;class MainWindow : Window
{private Button button;private TextBlock textBlock;public MainWindow(){button = new Button { Content = "Start Thread" };textBlock = new TextBlock { Text = "Waiting..." };button.Click += Button_Click;var stackPanel = new StackPanel();stackPanel.Children.Add(button);stackPanel.Children.Add(textBlock);Content = stackPanel;}private void Button_Click(object sender, RoutedEventArgs e){Thread thread = new Thread(UpdateTextBlock);thread.Start();}private void UpdateTextBlock(){for (int i = 0; i < 10; i++){// 使用 Dispatcher 將操作調度到 UI 線程textBlock.Dispatcher.Invoke(() => textBlock.Text = $"Count: {i}");Thread.Sleep(500); // 模擬工作}}
}class Program
{[STAThread]static void Main(){var app = new Application();app.Run(new MainWindow());}
}

解釋:

  • Dispatcher.Invoke:同步執行指定的操作,確保操作在 UI 線程上運行。
  • Dispatcher.BeginInvoke:異步執行指定的操作,不阻塞當前線程。

輸出效果:

點擊按鈕后,TextBlock?的文本會每 500 毫秒更新一次,顯示當前計數值。


3. 使用?SynchronizationContext

SynchronizationContext?是一種更通用的方式,適用于 Windows Forms 和 WPF,甚至其他框架(如 ASP.NET)。它允許你捕獲當前線程的上下文,并在需要時將其用于調度操作。

示例代碼:

using System;
using System.Reflection.Emit;
using System.Threading;
using System.Windows.Forms;
using static System.Net.Mime.MediaTypeNames;class Program : Form
{private Button button;private Label label;private SynchronizationContext _uiContext;public Program(){button = new Button { Text = "Start Thread", Dock = DockStyle.Top };label = new Label { Text = "Waiting...", Dock = DockStyle.Fill };button.Click += Button_Click;Controls.Add(label);Controls.Add(button);// 捕獲 UI 線程的上下文_uiContext = SynchronizationContext.Current;}private void Button_Click(object sender, EventArgs e){Thread thread = new Thread(UpdateLabel);thread.Start();}private void UpdateLabel(){for (int i = 0; i < 10; i++){// 使用 SynchronizationContext 將操作調度到 UI 線程_uiContext.Post(_ => label.Text = $"Count: {i}", null);Thread.Sleep(500); // 模擬工作}}[STAThread]static void Main(){Application.EnableVisualStyles();Application.Run(new Program());}
}

解釋:

  • SynchronizationContext.Current:捕獲當前線程的上下文(通常是 UI 線程的上下文)。
  • Post:異步執行指定的操作。
  • Send:同步執行指定的操作。

輸出效果:

點擊按鈕后,label?的文本會每 500 毫秒更新一次,顯示當前計數值。

?

?

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/76100.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76100.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/76100.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

TCP 和 UDP 可以使用同一個端口嗎?

TCP 和 UDP 可以使用同一個端口嗎&#xff1f; 前言 在深入探討 TCP 和 UDP 是否可以使用同一個端口之前&#xff0c;我們首先需要理解網絡通信的基本原理。網絡通信是一個復雜的過程&#xff0c;涉及到多個層次的協議和機制。在 OSI 模型中&#xff0c;傳輸層是負責端到端數…

RVOS-2.基于NS16550a ,為os添加終端交互功能。

2.1 實驗目的 為os添加uart功能&#xff0c;通過串口實現開發板與PC交互。 2.1 硬件信息 QEMU虛擬SoC含有 虛擬NS16550A設備 。 不同的地址線組合&#xff08;A2、A1、A0&#xff09;對應的讀寫模式和寄存器如下所示&#xff1a; 2.2 NS16550a 的初始化 線路控制寄存器&#…

java導入excel更新設備經緯度度數或者度分秒

文章目錄 一、背景介紹二、頁面效果三、代碼0.pom.xml1.ImportDevice.vue2.ImportDeviceError.vue3.system.js4.DeviceManageControl5.DeviceManageUserControl6.Repeater7.FileUtils8.ResponseModel9.EnumLongitudeLatitude10.詞條 四、注意點本人其他相關文章鏈接 一、背景介…

【力扣hot100題】(080)爬樓梯

讓我們掌聲恭迎動態規劃的始祖—— 最基礎的動態規劃&#xff0c;原始方法是維護一個數組&#xff0c;每次記錄到該階梯的方案數量&#xff0c;每次的數量是到上一個階梯的方案數量加上到上上一階梯的方案數量&#xff0c;因為只有兩種走法。 進階可以優化空間復雜度&#xf…

CVE-2025-24813 漏洞全解析|Apache Tomcat 關鍵路徑繞過與RCE

CVE-2025-24813 漏洞全解析&#xff5c;Apache Tomcat 關鍵路徑繞過與RCE 作者:Factor .Poc作者:iSee857 CVE-2025-24813 漏洞全解析&#xff5c;Apache Tomcat 關鍵路徑繞過與RCE一、漏洞概述二、影響版本三、漏洞原理&#x1f3af; 利用流程&#xff08;兩步&#xff09;&am…

初識Linux:常見指令與權限的理解,以及相關衍生知識

目錄 前言 關于linux的簡介 代碼開源 網絡功能強大 系統工具鏈完整 一、Linux下的基本指令 1.ls指令 2.pwd指令 3.cd指令 4.whoami指令 5.touch指令 6.mkdir指令 7.rm指令 8.man指令 9.cp指令 10.mv指令 11.nano指令 12.cat指令 13.tac指令 14.more指令 15.less指令 16.head指令…

JVM虛擬機篇(七):JVM垃圾回收器全面解析與G1深度探秘及四種引用詳解

JVM垃圾回收器全面解析與G1深度探秘及四種引用詳解 JVM虛擬機&#xff08;七&#xff09;&#xff1a;JVM垃圾回收器全面解析與G1深度探秘及四種引用詳解一、JVM有哪些垃圾回收器1. Serial回收器2. ParNew回收器3. Parallel Scavenge回收器4. Serial Old回收器5. Parallel Old回…

革新電銷流程,數企云外呼開啟便捷 “直通車”

在當今競爭激烈的商業環境中&#xff0c;電銷作為一種重要的營銷手段&#xff0c;依舊在企業的客戶拓展與業務增長中扮演著關鍵角色。然而&#xff0c;傳統電銷流程常常面臨諸多困擾&#xff0c;像是封卡封號風險、接通率不理想、客戶開發與管理艱難以及銷售考核復雜等問題&…

適合工程建筑行業的OA系統有什么推薦?

工程行業具有項目周期長、協作鏈條復雜等特性&#xff0c;傳統管理模式下的 “人治”“紙質化” 弊端日益凸顯。OA 系統作為數字化管理的核心載體&#xff0c;通過流程標準化、數據可視化&#xff0c;精準解決工程行業項目管理核心痛點。 泛微 e-office 深度聚焦工程場景&#…

車載刷寫架構 --- ECU收到相同的blockSequenceCounter數據包的思考

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 周末洗了一個澡,換了一身衣服,出了門卻不知道去哪兒,不知道去找誰,漫無目的走著,大概這就是成年人最深的孤獨吧! 舊人不知我近況,新人不知我過…

C++ RAII 的用途及業務代碼實現案例

C RAII 的用途及業務代碼實現案例 RAII 的核心概念 RAII (Resource Acquisition Is Initialization&#xff0c;資源獲取即初始化) 是 C 的核心編程范式&#xff0c;其核心思想是&#xff1a; 資源獲取與對象構造綁定資源釋放與對象析構綁定利用 C 對象生命周期自動管理資源…

黑馬 SpringAI+DeepSeek 實戰:從對話機器人到企業級知識庫的大模型開發全攻略

附完整代碼 項目案例&#xff0c;3 天吃透大模型應用開發核心技術 需要完整項目學習視頻以及源碼的私信博主&#xff0c;謝謝~大家一起加油吶&#xff01;&#xff01; 01.認識AI和大模型 小結 AI的發展過程 符號主義 機器學習 深度學習——自然語言處理&#xff08;NLP…

共工新聞社與韓國新華報社達成合作

在當下媒體融合浪潮奔涌的時代背景下&#xff0c;大灣區經濟網戰略媒體香港共工新聞社與韓國新華報社順利簽署合作協議&#xff0c;攜手為傳播全球化進程以及海外華文媒體從單一媒體向多媒體的內涵拓展&#xff0c;乃至區域經濟協同與文化融合發展貢獻力量。 締結友好華文媒體協…

嵌入式Linux驅動——3 總線設備驅動模型

目錄 1.總線設備驅動模型 1.1 總線設備驅動模型 1.2 設備樹 1.3 platform_device 和 platform_driver 的匹配規則 1.3.1 最先比較 1.3.2 然后比較 1.3.3 最后比較 2.LED 模板驅動程序的改造&#xff1a;總線設備驅動模型 1.總線設備驅動模型 在前面的 led 驅動程序中…

操作系統常用命令

邏輯卷創建及掛載步驟&#xff1a; vgcreate vg_app /dev/sda //在sda盤上創建vg_app卷組 lvcreate -L 50G -n lv_mysql vg_app //在vg_app卷組上創建邏輯卷lv_mysql mkfs.xfs /dev/vg_app/lv_mysql //對lv_mysql 邏輯卷創建文件系統 mkdir mysql //創建mysql目錄 ech…

Git 的進階功能和技巧

1、分支的概念和使用 1.1、什么是分支&#xff1f; 分支&#xff08;Branch&#xff09;是在版本控制中非常重要的概念。幾乎所有版本控制系統都支持某種形式的分支。在 Git 中&#xff0c;分支是 Git 強大功能之一&#xff0c;它允許我們從主開發線分離出來&#xff0c;在不…

mapbox基礎,加載F4Map二維地圖

????? 主頁: gis分享者 ????? 感謝各位大佬 點贊?? 收藏? 留言?? 加關注?! ????? 收錄于專欄:mapbox 從入門到精通 文章目錄 一、??前言1.1 ??mapboxgl.Map 地圖對象1.2 ??mapboxgl.Map style屬性二、??F4Map 簡介2.1 ??技術特點2.2 ??核…

Conda使用方法詳解

Conda是一個開源的包管理和環境管理系統&#xff0c;主要用于Python/R等科學計算領域&#xff0c;可以輕松管理不同項目的依賴關系。以下是Conda的詳細使用方法&#xff1a; 一、安裝與配置 1.安裝Miniconda/Anaconda Miniconda是精簡版&#xff0c;只包含conda和Python Ana…

Unity ViewportConstraint

一、組件功能概述 ViewportConstraint是一個基于世界坐標的UI邊界約束組件&#xff0c;主要功能包括&#xff1a; 將UI元素限制在父容器范圍內支持自定義內邊距&#xff08;padding&#xff09;可獨立控制水平和垂直方向的約束 二、實現原理 1. 邊界計算&#xff08;世界坐…

代碼隨想錄-動態規劃24

leetcode-300-最長遞增子序列 dp[i]表示i之前包括i的以nums[i]結尾的最長遞增子序列的長度 dp[j]是(0,i-1)不包括i的以nums[i-1]結尾的最長遞增子序列長度 int lengthOfLIS(int* nums, int numsSize) {if(numsSize < 1)return numsSize;int dp[numsSize];for(int i 0 ; i &…