Windows 應用程式的工作列通知區常駐與 Toast 通知 —— NotifyIcon 的陷阱與 AppNotification 的選型
· Go Komura · 工作列通知區, NotifyIcon, Toast 通知, AppNotification, Windows App SDK, WinForms, WPF, C#, .NET, 常駐應用程式, Windows 開發, 技術諮詢
「處理完成後想不打擾地告知使用者」、「即使關閉視窗也希望監控持續進行」──在業務應用程式中,常駐於工作列通知區與 Toast 通知是經常一起出現的經典需求。然而一旦真正動手實作,就會不斷冒出各種細節問題:原本應該關閉的應用程式卻無法結束、重新啟動檔案總管後圖示消失、開發階段能正常顯示的 Toast 到了客戶端卻不會出現。
而且 Toast 通知從 UWP 時代開始就一直在經歷 API 的世代交替。搜尋到的範例程式碼,如今很可能已經是僅維護等級的舊 API,這種情況並不少見。本文將結合實務上常見的陷阱,整理以 NotifyIcon 為基礎的通知區常駐設計,以及截至 2026 年 Toast 通知 API 的選型思路。
1. 先說結論
- 通知區常駐的基礎至今仍是
System.Windows.Forms.NotifyIcon。必須同時設定Icon並將Visible設為true才會顯示(預設值為false)。工具提示Text在 .NET 6 之後最長為 127 個字元(在此之前為 63 個字元)。12 - WPF 中不存在對應 NotifyIcon 的控制項。官方對照表中明確寫著「沒有對應控制項」。在 csproj 中同時啟用
UseWPF與UseWindowsForms,使用 WinForms 的 NotifyIcon,是最小化的組態方案。34 - 「即使按下關閉按鈕也保留在通知區」的做法是在
FormClosing中將e.Cancel設為true並呼叫Hide()。務必另外提供一個明確的結束方式(例如通知區選單中的「結束」),並檢查CloseReason,以免妨礙作業系統的關機流程。5 - 截至 2026 年,Toast 通知 API 的官方建議已經很明確。無論是 WinForms、WPF 還是非封裝應用程式,都建議使用 Windows App SDK 的
AppNotificationManager(NuGet 套件:Microsoft.WindowsAppSDK)。Windows.UI.Notifications處於「僅維護」狀態,Community Toolkit 的通知元件已被封存。67 - Windows App SDK 的 AppNotification 即使是沒有封裝識別碼的一般 exe 也能使用。
AppNotificationManager.Default.Register()會自動完成 COM 伺服器註冊以及相當於 AUMID 的處理,因此不再需要舊世代方案所需的開始功能表捷徑設定。不過,從提升為系統管理員權限的處理序發送的通知不受支援。8910 - Toast 並非「一定會被使用者看到」的手段。在開啟專注輔助(零打擾)期間,不會以橫幅方式顯示,而是直接進入通知中心,使用者也可以在設定中直接關閉通知功能。設計上不應將「Toast 一定會顯示」當作業務流程的前提。1112
- 無法從 Windows 服務發送 Toast(工作階段 0 中沒有 UI)。標準做法是在使用者工作階段端放置常駐於通知區的代理程式,透過 IPC 進行溝通。13
2. 以 NotifyIcon 為基礎的通知區常駐
在工作列右端的通知區顯示圖示的元件是 System.Windows.Forms.NotifyIcon。持續在背景執行的應用程式會將其作為狀態顯示與操作選單的進入點來使用。1
private NotifyIcon _trayIcon = null!;
private void InitializeTrayIcon()
{
var menu = new ContextMenuStrip();
menu.Items.Add("開啟", null, (_, _) => ShowMainWindow());
menu.Items.Add(new ToolStripSeparator());
menu.Items.Add("結束", null, (_, _) => ExitApplication());
_trayIcon = new NotifyIcon
{
Icon = Properties.Resources.AppIcon, // Icon 為必填,未設定則不會顯示
Text = "訂單監控工具 ── 執行中", // 工具提示(.NET 6+ 最多 127 個字元)
ContextMenuStrip = menu,
Visible = true // 預設為 false,設為 true 後才會顯示
};
_trayIcon.DoubleClick += (_, _) => ShowMainWindow();
}
需要留意的重點有三個。
- 只有
Icon與Visible = true兩者都具備時才會顯示。Visible的預設值是false。1 Text有長度限制。 .NET 6 之後最長為 127 個字元,.NET 5 之前的版本以及 .NET Framework 則為 63 個字元,超出會擲回ArgumentException。把過多狀態資訊塞進工具提示裡,可能會在某個時刻突然導致例外崩潰。214- 右鍵選單透過
ContextMenuStrip關聯。 通知區圖示往往是使用者唯一能看到的進入點,因此至少要提供「開啟」與「結束」這兩個選項。15
還有一點,通知區圖示不僅是通知的進入點,同時也是一個持續可見的狀態顯示。由於 Icon 與 Text 可以在執行期間隨時替換,若能做到「正常時使用一般圖示、發生異常時切換為警告圖示」、「在工具提示中顯示最近一次處理時間」之類的設計,反映當前狀態,那麼即使使用者錯過了 Toast,只要看一眼圖示也能知道目前的狀態。可以說 Toast 負責的是「告知那個瞬間發生的事」,而通知區圖示負責的是「持續顯示目前的狀態」,兩者的角色分工不同。
3. 「關閉後仍留在通知區」的設計與正確的結束路徑
常駐應用程式的經典行為是「按下 × 按鈕關閉後,處理序並不會結束,而是留在通知區」。這個實作本身只需在 FormClosing 中取消關閉即可簡單寫出,但若寫得粗糙,就會導致兩種事故:沒有任何方式可以結束應用程式,以及妨礙 Windows 的關機流程。
private bool _exitRequested;
protected override void OnFormClosing(FormClosingEventArgs e)
{
// 若使用者只是按下 × 按鈕,則隱藏而非關閉
if (!_exitRequested && e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
return;
}
// 透過「結束」選單或作業系統關機等情況則正常關閉
base.OnFormClosing(e);
}
private void ExitApplication()
{
_exitRequested = true;
_trayIcon.Visible = false;
_trayIcon.Dispose();
Close();
}
FormClosing 會在表單關閉之前觸發,可以透過將 e.Cancel 設為 true 來取消關閉。此時務必檢查 CloseReason。正確的做法是只在 UserClosing(使用者操作)時取消關閉,而對於 WindowsShutDown 等由作業系統引發的關閉則不應取消。516
WPF 中的思路也是一樣的,官方文件中直接列出了在 Window.Closing 中將 e.Cancel 設為 true 並呼叫 Hide() 的隱藏模式。17
另外,常駐應用程式就其性質而言,總是伴隨著「重複啟動會導致同一事件被處理兩次」的風險。防止重複啟動的做法我們在「防止 Windows 應用程式重複啟動 ── 具名互斥鎖與重複啟動時的啟用處理」中整理過。而常駐應用程式內部(監控迴圈或定期處理)的建構方式,則可以參考「為什麼要在桌面應用程式中使用 .NET Generic Host 與 BackgroundService」這篇文章。
4. WPF 應用程式中的通知區常駐
WPF 中沒有對應 NotifyIcon 的控制項。這是官方「與 WinForms 控制項對應的 WPF 控制項」對照表中明確寫著「沒有對應控制項」的規格空白。3
現實中可行的方案有兩種。
| 方案 | 概要 | 判斷依據 |
|---|---|---|
| 併用 WinForms 的 NotifyIcon | 在 csproj 中同時啟用 UseWPF 與 UseWindowsForms |
無額外相依性。只需要圖示與選單時已經足夠 |
| 第三方(如 H.NotifyIcon 等) | 針對 WPF/WinUI 的通知區圖示實作 | 想用 XAML 撰寫、或需要更豐富的彈出效果時選用。需考量其並非 Microsoft 官方元件 |
前者只需在 SDK 樣式的專案檔中並列寫出兩個屬性即可。4
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
之後就可以直接在 WPF 的 Application 中像平常一樣 new 出 System.Windows.Forms.NotifyIcon 來使用。雖然選單部分會變成 WinForms 的 ContextMenuStrip,但對於通知區選單這種程度的需求來說,實際使用上幾乎不會造成問題。
5. 通知區圖示的三個陷阱
5.1. 圖示預設會被收進「隱藏的圖示」中 ── 無法透過程式強制常駐顯示
「希望自己的圖示始終顯示在工作列上」這樣的需求必然會出現,但這一點無法透過程式實現。官方文件明確寫明:圖示預設會被加入溢位(隱藏圖示)區域,能夠決定是否將其提升到通知區(始終顯示)的只有使用者本人。18
在 Windows 11 中,使用者可以在「設定 → 個人化 → 工作列 → 其他系統匣圖示」中依應用程式逐一開關。若始終顯示在營運上很重要,正確的做法是在安裝說明或說明文件中寫明「請開啟此設定」。
5.2. 檔案總管重新啟動後圖示會消失 ── TaskbarCreated
當檔案總管因當機或重新啟動而重新建立工作列時,已經註冊的通知區圖示會消失。殼層(Shell)在建立工作列時會向所有最上層視窗廣播 TaskbarCreated 訊息,因此在 Win32 層級的規則是:收到該訊息後應重新註冊圖示。19
WinForms 的 NotifyIcon 會在內部處理這則訊息並自動重新註冊,因此通常不需要特別在意。若是自行透過 P/Invoke 呼叫 Shell_NotifyIcon(或是維護帶有通知區圖示的原生應用程式),就需要確認是否已經加入了這段重新註冊的邏輯。「重新啟動檔案總管後只有圖示消失了,處理序依然存活」這種症狀,基本上就是這個原因。
5.3. 忘記 Dispose 會導致圖示殘留
從 Win32 的角度來看,不再需要通知區圖示時必須明確刪除(NIM_DELETE),在 WinForms 中這項工作由 NotifyIcon.Dispose() 負責。18 請確保結束路徑一定會經過 Visible = false → Dispose()。若處理序因異常終止等原因而未執行刪除,圖示就會一直以「幽靈圖示」的形式殘留,直到滑鼠游標移到上面才消失──這是任何常駐應用程式開發者遲早都會遇到的場景。
按照第 3 章的程式碼那樣,在「結束」選單的處理中先清理通知區圖示,再呼叫 Close(),是比較安全的做法。
6. Toast 通知 ── 該使用哪一種 API
Toast 通知是顯示在螢幕右下角的橫幅,並會保留在通知中心的通知形式。從桌面應用程式發送 Toast 的方式在過去十年間經歷了多次世代交替,搜尋結果中至今仍殘留著大量過時的範例。以下是截至 2026 年的整理。6
| API | NuGet / 命名空間 | 定位(截至 2026 年) | 判斷 |
|---|---|---|---|
| Windows App SDK AppNotification | Microsoft.WindowsAppSDK / Microsoft.Windows.AppNotifications |
對 WinForms、WPF、非封裝 Win32 皆為官方建議 | 新專案選它 |
| Community Toolkit Notifications | Microsoft.Toolkit.Uwp.Notifications | 已封存。 不再包含於目前的 Community Toolkit 中,最後一次更新在 2022 年 | 僅用於維護現有程式碼。不要用於新專案 |
| 直接使用 WinRT | Windows.UI.Notifications(透過 Windows TFM) |
僅維護狀態。 主要建議用於 UWP | 不要用於新專案 |
| NotifyIcon.ShowBalloonTip | System.Windows.Forms |
氣球通知 API。在 Windows 10 中以 Toast 形式顯示,在 Windows 11 中則為暫時顯示,不會保留在通知中心 | 簡單用途可以接受,但要注意其行為會隨作業系統版本而變化 |
有兩點補充說明。
ShowBalloonTip屬於「雖然是舊 API,但其實並未過時」的一類。 在 Windows 10 中,氣球會以 Toast 的形式呈現,在被關閉之前會一直保留在通知中心。而在 Windows 11 中,它更接近傳統氣球那種暫時顯示的效果,消失之後不會保留在通知中心。顯示時間長度(timeout)的設定現在也會被忽略,而是遵循作業系統端的協助工具設定。若只是通知區常駐應用程式想輕量地告知自身的狀態變化,這已經足夠;但若希望「在通知中心留下歷史記錄」,就要記住在 Windows 11 上它派不上用場。20- 從桌面應用程式直接使用原生
Windows.UI.Notifications的經典做法,存在一個著名的限制:必須在開始功能表中註冊帶有 AUMID(AppUserModelID)的捷徑,否則 Toast 無法顯示。21 而下一章介紹的 AppNotification 正是為了消除這項麻煩而出現的。
7. Windows App SDK AppNotification 的最小實作
Windows App SDK 的應用程式通知 API 即使是未封裝的一般 exe,也能在沒有封裝識別碼的情況下運作。8 專案端的前提條件包括:使用 Windows 專屬的 TFM(例如 net8.0-windows10.0.19041.0)、<WindowsPackageType>None</WindowsPackageType>(適用於非封裝的情況),以及部署目標上已安裝 Windows App SDK 執行階段。9
using Microsoft.Windows.AppNotifications;
using Microsoft.Windows.AppNotifications.Builder;
// 啟動時執行一次即可。處理常式註冊 → Register() 的順序很重要
AppNotificationManager.Default.NotificationInvoked += (_, args) =>
{
// 點擊 Toast 或按下按鈕都會傳到這裡(透過 args.Arguments 判斷參數)
if (args.Arguments.TryGetValue("action", out var action) && action == "openLog")
{
ShowLogWindow();
}
};
AppNotificationManager.Default.Register();
// 顯示通知
var notification = new AppNotificationBuilder()
.AddText("訂單匯入已完成")
.AddText("新增 12 筆,錯誤 1 筆")
.AddButton(new AppNotificationButton("開啟記錄")
.AddArgument("action", "openLog"))
.BuildNotification();
AppNotificationManager.Default.Show(notification);
實際使用時需要注意以下幾點。
NotificationInvoked的訂閱要在Register()之前完成。 若順序顛倒,當透過點擊 Toast 啟動應用程式時,可能會漏掉這個事件。9- 應用程式結束前應呼叫
AppNotificationManager.Default.Unregister()。 官方文件明確指出,在結束前呼叫 Unregister 清理資源,可以確保之後點擊通知時應用程式能夠正確啟動。請將其與NotifyIcon.Dispose()一併加入第 3 章「結束」選單的處理邏輯中(若是要解除安裝一個今後完全不再使用通知功能的應用程式,還另外提供了會一併清除註冊資訊的UnregisterAll())。22 - 對於非封裝應用程式,
Register()會自動完成 COM 伺服器註冊以及相當於 AUMID 的處理。舊世代(原生 WinRT)所需要的捷徑設定與登錄檔登記都不再需要。若改用 MSIX 封裝,反而需要在 Package.appxmanifest 中宣告 COM 啟動器。9 - 若選擇隨附 Windows App SDK 執行階段的 self-contained 部署方式,需要格外注意。
AppNotificationManager相依於 Windows App SDK 的 Singleton 套件,官方針對從 self-contained 應用程式呼叫時的注意事項有專門說明。在決定部署方式之前請先確認這一點。23 - 不支援從提升為系統管理員權限的處理序發送通知。
Show不會擲回例外,而是靜默失敗,因此這是一個很難察覺原因的問題。對於需要提升權限的應用程式,可以考慮僅將通知功能交由未提升權限的處理序來處理(這種分離思路我們在「Windows 應用程式中如何將「需要系統管理員權限的處理」單獨分離出來的具體做法」中有過介紹)。10 - 當 Toast 被點擊時,若應用程式尚未執行,會新啟動一個處理序。 在通知區常駐應用程式中,需要將其與防止重複啟動的機制(參見第 3 章)結合起來,設計為將處理轉交給既有執行個體的方式。
8. 進階用法 ── 帶進度列的 Toast 與通知的清理
8.1. 在 Toast 內部更新進度 ── UpdateAsync
對於「正在匯入 120 筆訂單資料」這類耗時較長的處理,正確的做法不是反覆重新彈出通知,而是在同一則 Toast 內部改寫其中的進度列。AppNotification 為此提供了資料繫結與更新機制。24
整個流程分為三個階段。首先透過 Bind 系列方法將進度列的各個值設為預留位置,並透過 Tag(若有需要也可加上 Group)使通知可被識別,然後將其顯示出來。此後只需將 AppNotificationProgressData 傳給 UpdateAsync,就只會改寫已經顯示出來的那則 Toast 的內容。
const string tag = "import-progress";
const string group = "batch";
// 首次顯示:將值綁定,並在 Progress 設定初始值
var notification = new AppNotificationBuilder()
.AddText("正在匯入訂單資料")
.AddProgressBar(new AppNotificationProgressBar()
.BindStatus().BindValue().BindValueStringOverride())
.SetTag(tag)
.SetGroup(group)
.BuildNotification();
notification.Progress = new AppNotificationProgressData(1) // 序號
{
Status = "匯入中...", Value = 0, ValueStringOverride = "0/120 筆"
};
AppNotificationManager.Default.Show(notification);
// 每次進度更新時:遞增序號並更新
var data = new AppNotificationProgressData(2)
{
Status = "匯入中...", Value = 45 / 120.0, ValueStringOverride = "45/120 筆"
};
await AppNotificationManager.Default.UpdateAsync(data, tag, group);
序號是表示更新順序的非零整數,若多個更新前後交錯到達,會顯示編號最大的那一個。這項機制是為了防止非同步處理並行發送更新時數值倒退回舊狀態,因此請務必讓它單調遞增。25
8.2. 「更新」與「取代」的區別
用相同的 Tag 重新 Show 一則新通知來實現的「取代」,看起來同樣能更新外觀,但其行為與「更新」並不相同。24
| 取代(用相同 Tag 重新 Show) | 更新(UpdateAsync) | |
|---|---|---|
| 在通知中心內的位置 | 會移動到最前面 | 停留在原位 |
| 可修改的內容 | 包括版面配置在內的全部內容 | 僅限已繫結的進度列與最前面的文字 |
| 彈出提示 | 可以再次彈出 | 不會再次彈出(靜默改寫) |
| 使用者已經關閉通知的情況 | 總是會發送 | 會失敗 |
官方文件給出的建議也很明確:中間過程用更新,完成時用取代。 進度從 50% 變為 65% 時沒有必要每次都彈出提示,但「匯入完成(3 筆錯誤)」這樣的資訊,即使使用者已經關閉了進度通知,也希望能夠送達。因此,在完成時移除進度列、換成帶按鈕(如「查看錯誤內容」)的另一種版面配置並加以取代,是實務中常見的做法。
8.3. 清理通知中心裡殘留的舊通知
Toast 會一直堆積在通知中心,直到使用者將其清除。若常駐應用程式每天發送多則通知,通知中心就會被自家應用程式的舊通知塞滿,反而會把重要通知埋沒掉。
只要附加了 Tag / Group,就可以透過程式碼刪除自家應用程式的通知(可依標籤、依群組,或全部刪除)。同時也可以設定有效期限,因此建議在設計通知發送方式的同時,一併制定諸如「進度類通知在完成時清除」、「資訊類通知縮短有效期限」這樣的清理規則。26
9. 將通知「未能送達」的情況納入設計考量
Toast 通知並非可靠的傳達手段。存在一些完全屬於正常情況、但通知不會顯示或不會被注意到的路徑。
| 情況 | 行為 |
|---|---|
| 開啟了專注輔助(零打擾) | 不會顯示橫幅,通知會直接進入通知中心 |
| 依應用程式或整個系統關閉了通知 | 不會顯示 |
| 來自提升為系統管理員權限處理序的 AppNotification | 不受支援,不會顯示 |
| 正在執行全螢幕應用程式等情況 | 作業系統可能會自行判斷並加以抑制 |
零打擾模式在 Windows 10 中稱為「專注輔助(Focus assist)」,在 Windows 11 中則透過「設定 → 系統 → 通知」中的「零打擾」切換開關來控制,可由使用者或群組原則控制。甚至還有規則規定,在系統升級後的一小時內會自動開啟該模式。11 專注模式的行為並不是「不發送通知」,而是「抑制橫幅顯示,直接發送到通知中心」,因此只要使用者之後開啟通知中心,依然能夠看到。12
對設計的啟示很簡單:
- 應把 Toast 當作「使用者一旦注意到就能更快回應」的輔助管道,業務上必需的資訊務必確保可以在應用程式內的畫面或清單中查閱。 不要把「Toast 應該已經彈出了」當作業務流程的前提。
- 對於確實需要強制中斷使用者的通知(例如緊急停止警告),存在透過
AppNotificationScenario.Urgent(重要通知)在取得使用者許可的前提下突破零打擾模式的機制,27 但若濫用,使用者就會直接關閉該應用程式的全部通知。應僅將其用於真正緊急的情況。
關於如何將外部裝置的異常等狀態呈現在畫面上這個更廣泛的話題,也可以參考「外部裝置狀態確認與顯示的最佳實務」。
10. 需要從 Windows 服務發送通知時
「常駐服務偵測到異常後,希望透過 Toast 告知已登入的使用者」是一個很自然的需求,但無法直接從服務發送 Toast。 自 Windows Vista 起,服務執行於名為工作階段 0 的專用工作階段中,不具備與使用者互動的 UI。13
標準做法是將 UI 分離到使用者工作階段端的處理序中。
[工作階段 0] [使用者工作階段]
Windows 服務 <-- IPC --> 通知區常駐代理程式
(監控・業務處理) 具名管道等 (NotifyIcon + AppNotification)
- 服務專注於監控與業務處理,將需要通知的事件透過 IPC 發送給代理程式。
- 代理程式是在使用者登入時啟動的通知區常駐應用程式(也就是本文第 2~8 章所講的內容本身),負責將接收到的事件以 Toast 或圖示變化的形式呈現出來。
- IPC 方式的選型可以參考「如何選擇 Windows 處理序間通訊方式」,服務端的實作方法可以參考「Windows 服務的製作方法與維運」,工作階段機制本身的原理可以參考「如何理解 Windows 的工作階段隔離 ── 工作階段 0、RDP 與多使用者同時執行」。
在多位使用者同時登入的環境中(RDP、共用終端機),代理程式會依工作階段各自執行一份。事先在需求階段就明確「應該通知誰」(是所有人,還是特定使用者),可以讓設計更加穩定,不至於搖擺不定。
11. 總結
- 通知區常駐的基礎是
NotifyIcon。透過Icon+Visible = true顯示,Text有 127 字元的長度限制(.NET 6 以上),結束路徑中要確保執行Dispose()。圖示與工具提示也可以作為常顯的「目前狀態」展示來使用。 - 「按 × 關閉後仍保留在通知區」透過取消
FormClosing來實現,但只應在CloseReason.UserClosing時取消,並務必提供一個明確的「結束」選單。 - 圖示能否始終顯示只能由使用者決定。檔案總管重新啟動導致圖示消失的問題,透過在
TaskbarCreated時重新註冊來解決(WinForms 會自動處理)。 - 新開發的 Toast 通知功能,唯一的選擇就是 Windows App SDK 的
AppNotificationManager。即使是沒有封裝識別碼的 exe 也能運作,Register()會自動完成繁瑣的註冊工作。Community Toolkit 通知已被封存,原生 WinRT 則僅處於維護狀態。 - 對於耗時較長的處理,中間進度應透過
UpdateAsync靜默改寫同一則 Toast 中的進度列,完成時則透過取代來彈出提示──這是標準做法。同時應附加Tag/Group,並將清理舊通知也納入設計範圍。 - Toast 是一種輔助管道,可能因為零打擾模式或通知被關閉而無法送達。設計上應確保必需資訊始終可以在應用程式內確認,同時也要留意來自提升權限處理序的通知不受支援。
- 來自服務的通知,透過「使用者工作階段端的通知區常駐代理程式 + IPC」這種分離架構來實現。
相關文章
- 防止 Windows 應用程式重複啟動 ── 具名互斥鎖與重複啟動時的啟用處理
- Windows 服務的製作方法與維運 ── 與工作排程器的取捨,以及將 BackgroundService 服務化
- 如何選擇 Windows 處理序間通訊方式 ── 具名管道 / TCP / gRPC / 共用記憶體 / COM 判斷表
- 如何理解 Windows 的工作階段隔離 ── 工作階段 0、RDP 與多使用者同時執行
- 為什麼要在桌面應用程式中使用 .NET Generic Host 與 BackgroundService
- Windows 應用程式中如何將「需要系統管理員權限的處理」單獨分離出來的具體做法
- 外部裝置狀態確認與顯示的最佳實務 - 不止步於「已連線」的設計
相關諮詢領域
合同會社小村軟體(KomuraSoft LLC)承接通知區常駐型監控工具與通知代理程式的設計與實作、與 Windows 服務組合的常駐系統架構評估,以及既有常駐應用程式的故障排除。
參考連結
-
Microsoft Learn, NotifyIcon Class / NotifyIcon.Visible Property。關於 NotifyIcon 是在通知區顯示圖示的元件、需要設定 Icon 屬性,以及 Visible 預設值為 false 的說明。 ↩ ↩2 ↩3
-
Microsoft Learn, NotifyIcon.Text Property。關於工具提示文字的長度上限,以及超出時會擲回 ArgumentException 的說明。 ↩ ↩2
-
Microsoft Learn, Windows Forms Controls and Equivalent WPF Controls。關於 WPF 中不存在對應 NotifyIcon 的控制項的說明。 ↩ ↩2
-
Microsoft Learn, MSBuild properties for .NET desktop projects。關於可以同時啟用 UseWPF 與 UseWindowsForms 的說明。 ↩ ↩2
-
Microsoft Learn, Form.FormClosing Event。關於該事件在表單關閉之前觸發,並可透過 Cancel 屬性取消關閉的說明。 ↩ ↩2
-
Microsoft Learn, Windows notifications overview。依應用程式類型劃分的建議通知 API 對照表,涵蓋對 WinForms/WPF/非封裝 Win32 的 Windows App SDK 建議,以及 Windows.UI.Notifications 處於「僅維護」狀態的說明。 ↩ ↩2
-
Microsoft Learn, Windows Community Toolkit — Notifications (archive)。關於 Notifications 元件已被封存、由 Windows App SDK 的應用程式通知取代的說明。 ↩
-
Microsoft Learn, Use the Windows App SDK in an existing project。關於 Windows App SDK 的應用程式通知 API 同時支援封裝與非封裝兩種型態,且不要求封裝識別碼的說明。 ↩ ↩2
-
Microsoft Learn, Quickstart: App notifications in the Windows App SDK。關於 AppNotificationManager/AppNotificationBuilder 的用法、非封裝應用程式中 Register() 帶來的自動註冊、應在 Register() 之前訂閱 NotificationInvoked,以及封裝應用程式中資訊清單記載方式的說明。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, App notifications overview。關於從提升為系統管理員權限的應用程式發送通知不受支援的說明。 ↩ ↩2
-
Microsoft Learn, Notifications don’t appear or aren’t displayed。關於零打擾模式(Quiet Hours / Focus Assist)對通知的抑制、系統升級後一小時內的自動開啟,以及通知未顯示時的排查步驟的說明。 ↩ ↩2
-
Microsoft Learn, Focus assist。關於專注模式會抑制通知的橫幅顯示、並將其直接發送到通知中心這項機制的說明。 ↩ ↩2
-
Microsoft Learn, Service Changes for Windows Vista / Interactive Services。關於工作階段 0 專用於服務、不支援與使用者互動,以及若要向使用者顯示內容,建議使用 CreateProcessAsUser 在使用者工作階段中建立處理序並透過 IPC 溝通這項做法的說明。 ↩ ↩2
-
Microsoft Learn, NotifyIcon.Text maximum text length increased。關於 .NET 6 中 Text 的最大長度從 63 個字元擴增到 127 個字元這項重大變更的說明。 ↩
-
Microsoft Learn, FormClosingEventArgs Class。關於可以透過 CloseReason 判斷表單關閉原因的說明。 ↩
-
Microsoft Learn, How to close a window or dialog box (WPF)。關於在 WPF 的 Closing 事件中將 e.Cancel 設為 true 並呼叫 Hide() 實現隱藏模式的說明。 ↩
-
Microsoft Learn, Notification Area (Windows Shell)。關於圖示預設會被加入溢位區域、只有使用者才能決定是否提升到通知區,以及不再需要的圖示必須透過 NIM_DELETE 刪除的說明。 ↩ ↩2
-
Microsoft Learn, The Taskbar。關於工作列建立時會廣播 TaskbarCreated 訊息、收到該訊息後應重新註冊通知區圖示的說明。 ↩
-
Microsoft Learn, Shell_NotifyIconW function。關於氣球通知在 Windows 10 中以 Toast(橫幅)形式顯示並保留在通知中心、在 Windows 11 中則為暫時顯示且不保留在通知中心,以及顯示時間長度遵循協助工具設定的說明。 ↩
-
Microsoft Learn, Send a local toast notification from a C# app (Win32/傳統 API)。關於使用原生 ToastNotificationManager 時,必須在開始功能表中註冊帶 AUMID 的捷徑的說明。 ↩
-
Microsoft Learn, AppNotificationManager.Register Method。關於應在應用程式結束前呼叫 Unregister 以清理資源、確保之後的通知啟動能夠正確進行,以及在今後完全不再使用通知功能時應使用 UnregisterAll 清除註冊資訊的說明。 ↩
-
Microsoft Learn, AppNotificationManager.UpdateAsync Method / Windows App SDK deployment architecture。關於 AppNotificationManager 類別相依於 Windows App SDK 的 Singleton 套件,以及從 self-contained 部署的應用程式呼叫時的注意事項的說明。 ↩
-
Microsoft Learn, App notification progress bar and data binding。關於透過 Bind 方法實現資料繫結、透過 AppNotificationProgressData 與 UpdateAsync 實現更新,以及更新與取代的區別(在通知中心內的位置、可修改的範圍、彈出提示的再次顯示、使用者已關閉通知後的行為),以及完成時應考量使用取代的說明。 ↩ ↩2
-
Microsoft Learn, AppNotificationProgressData(UInt32) Constructor。關於序號是指定更新順序的非零整數、在收到多個更新資料時會顯示編號最大的資料的說明。 ↩
-
Microsoft Learn, Remove app notifications。關於附加標籤・群組、刪除已顯示的通知,以及設定有效期限的說明。 ↩
-
Microsoft Learn, App notification content。關於 scenario=”urgent” 的重要通知,在取得使用者許可的前提下會優先於零打擾模式設定的說明。 ↩
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
WinForms/WPF 應用程式的多語言化 ── resx、附屬組件與文化特性切換的實務
本文從實務角度整理 Windows 桌面應用程式的多語言化,包括 CurrentCulture 與 CurrentUICulture 的差異、resx 與附屬組件(Satellite Assembly)所構成的資源機制、WinForms 的 Localizable 屬性、W...
Windows 業務應用程式的列印與 PDF 輸出 ── System.Drawing.Printing / WPF / 報表函式庫的取捨
本文以要件別判斷表整理 PrintDocument 的 WinForms 列印、WPF 的 FlowDocument/FixedDocument 列印,以及 PDF 輸出的各種選項。並從實務角度說明分頁控制、DPI 落差等實作陷阱,以及從 Windows 服務列印、依賴預設...
在 WinForms/WPF 應用程式中導入 Entra ID 驗證 ── MSAL.NET 與 WAM 代理的實務架構
從實務角度整理在 WinForms/WPF 桌面應用程式中導入 Entra ID(原 Azure AD)驗證的做法。內容涵蓋公用客戶端的概念、ROPC 廢除現況、應用程式註冊、MSAL.NET 的 AcquireTokenSilent 模式、WAM 代理、權杖快取持久化,以...
委外・委託開發 Windows 應用程式前該整理的事項
在委外・委託開發 Windows 應用程式之前,整理既有軟體改版、設備整合、COM/ActiveX、發布與更新、維護等應留意的重點。
把 Generic Host / BackgroundService 帶進桌面應用程式的理由 - 啟動・壽命・graceful shutdown 的整理會輕鬆很多
整理把 .NET Generic Host 與 BackgroundService 帶進 WPF / WinForms 桌面應用程式的理由,把啟動、lifetime、graceful shutdown 集中於入口管理。透過 StartAsync / ExecuteAsync...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
UI 執行緒 & 計時器
整理 WPF / WinForms UI 執行緒、非同步流程、Dispatcher 使用、計時器判斷的主題頁面。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
技術諮詢 & 設計審查
協助釐清設計方向、架構邊界、生命週期責任,以及既有 Windows 資產的處理方式。
常見問題
整理諮詢這個主題時常見的問題。
- 在 WPF 中要如何製作工作列通知區常駐應用程式?
- 由於 WPF 中並不存在對應 NotifyIcon 的控制項,最小組態是在 csproj 中同時啟用 UseWPF 與 UseWindowsForms,使用 WinForms 的 NotifyIcon。若想採用 XAML 風格的寫法,H.NotifyIcon 等第三方函式庫也是一個選項,但請留意它並非 Microsoft 官方元件,採用與否需自行評估。
- Toast 通知應該使用哪一種 API?
- 以 2026 年的新開發而言,無論是 WinForms、WPF 還是非封裝應用程式,官方都建議使用 Windows App SDK 的 AppNotificationManager。Microsoft.Toolkit.Uwp.Notifications 已被封存,不再包含於目前的 Community Toolkit 中;而 WinRT 的 ToastNotificationManager 則處於僅維護狀態。若現有程式碼仍運作於後兩者之上,並不需要急著改寫,但沒有理由在新專案中選用它們。
- Toast 通知沒有顯示時應該檢查什麼?
- 首先要檢查作業系統端的設定。若開啟了「專注輔助」(零打擾),通知不會以橫幅方式顯示,而是直接進入通知中心;此外,若在設定應用程式中依應用程式或整個系統關閉了通知,同樣不會顯示。在實作面,常見原因包括:以系統管理員權限提升的處理序所發送的 AppNotification 不受支援而顯示失敗,以及在沒有 AUMID 的桌面應用程式中直接呼叫原生的 Windows.UI.Notifications 也不會顯示。
- 可以從 Windows 服務發送 Toast 通知嗎?
- 不可以。自 Windows Vista 起,服務執行於工作階段 0(Session 0)中,無法擁有與使用者互動的 UI。若需要通知使用者,標準做法是在使用者工作階段端放置一個常駐於通知區的代理程式處理序,服務與該代理程式之間透過具名管道等 IPC 方式溝通,由代理程式負責顯示通知。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。