經典 MSN Messenger 的右下角彈出通知視窗 (Toast Notification) 是桌面應用程式使用者體驗的里程碑。本文完整解析一個 WinForms 實作:無邊框視窗、Timer 驅動滑入/滑出動畫、螢幕工作區定位、最上層顯示、任務列隱藏,並延伸討論現代化替代方案 (WPF、WinUI 3、Windows 10/11 原生 Toast)。
public partial class ToastForm : Form
{
public ToastForm()
{
InitializeComponent();
// 關鍵視窗樣式
FormBorderStyle = FormBorderStyle.None; // 無標題列/邊框
TopMost = true; // 永遠置頂
ShowInTaskbar = false; // 不佔用任務列
StartPosition = FormStartPosition.Manual; // 手動定位
}
// 公開設定
public int HeightMax { get; set; } = 120;
public int WidthMax { get; set; } = 180;
public int StayTime { get; set; } = 5000; // 停留毫秒
// 三階段 Timer (Interval=10ms ≈ 100 FPS)
private readonly Timer _slideUpTimer = new Timer { Interval = 10 };
private readonly Timer _stayTimer = new Timer { Interval = 5000 };
private readonly Timer _slideDownTimer = new Timer { Interval = 10 };
}
private void SlideUp()
{
if (Height < HeightMax)
{
Height += 3; // 每幀增加 3px
Location = new Point(Location.X, Location.Y - 3); // 向上移動
}
else
{
_slideUpTimer.Stop();
_stayTimer.Interval = StayTime;
_stayTimer.Start();
}
}
private void _slideUpTimer_Tick(object sender, EventArgs e) => SlideUp();
private void _stayTimer_Tick(object sender, EventArgs e)
{
_stayTimer.Stop();
_slideDownTimer.Start();
}
private void SlideDown()
{
if (Height > 3)
{
Height -= 3;
Location = new Point(Location.X, Location.Y + 3); // 向下移動
}
else
{
_slideDownTimer.Stop();
Close(); // 釋放資源
}
}
private void _slideDownTimer_Tick(object sender, EventArgs e) => SlideDown();
public void ShowToast()
{
// 1. 計算右下角起始位置 (工作區右下 - 視窗寬 - 邊距)
Screen screen = Screen.PrimaryScreen; // 或 Screen.FromControl(this)
Rectangle wa = screen.WorkingArea;
Width = WidthMax;
Height = 0; // 初始高度 0
Location = new Point(wa.Right - WidthMax - 20, wa.Bottom - 34); // 34 = 任務列高度估值
// 2. 顯示並啟動滑入
Show();
_slideUpTimer.Start();
}
private void btnShowToast_Click(object sender, EventArgs e)
{
var toast = new ToastForm
{
HeightMax = 120,
WidthMax = 180,
StayTime = 5000
};
// 可動態設定內容
toast.Controls["lblMessage"].Text = "您有新訊息!";
toast.ShowToast();
}
private void FadeIn()
{
if (Opacity < 1.0) Opacity += 0.05;
else _fadeTimer.Stop();
}
private void FadeOut()
{
if (Opacity > 0) Opacity -= 0.05;
else { _fadeTimer.Stop(); Close(); }
}
搭配 SlideUp/Down 同時執行,效果更自然。
private void ToastForm_Click(object sender, EventArgs e)
{
// 開啟主視窗對應頁面
MainForm.Instance.ShowConversation(messageId);
Close(); // 立即關閉
}
public static class ToastManager
{
private static readonly Queue _queue = new();
private static ToastForm _current;
private static readonly object _lock = new();
public static void Enqueue(string title, string body, Action onClick)
{
lock (_lock)
{
_queue.Enqueue(new ToastData(title, body, onClick));
TryShowNext();
}
}
private static void TryShowNext()
{
if (_current != null || _queue.Count == 0) return;
_current = new ToastForm();
var data = _queue.Dequeue();
_current.SetContent(data.Title, data.Body);
_current.Click += (s, e) => { data.OnClick?.Invoke(); _current = null; TryShowNext(); };
_current.FormClosed += (s, e) => { _current = null; TryShowNext(); };
_current.ShowToast();
}
}
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| WinForms Timer + GDI+ | 零相依、.NET Framework 2.0+ 皆可、部署簡單 | 動畫卡頓 (Timer 精度低)、無 GPU 加速、高 DPI 模糊 | 維舊專案、輕量工具 |
| WPF + Storyboard | GPU 加速、向量縮放、Easing 函數、樣式/模板分離 | 需 .NET Core 3.0+/Framework 4.x、學習曲線 | 新專案、重視 UI 體驗 |
| WinUI 3 / Windows App SDK | 原生 Fluent Design、Windows 11 圓角/陰影、打包輕鬆 | 需 Windows 10 1809+、較大運行時 | 現代 Windows 應用 |
| Windows 10/11 原生 Toast (UWP API) | 系統整合 (動作中心)、支援圖片/按鈕/輸入、跨應用顯示 | 需打包身分 (MSIX/稀疏)、API 複雜 | 需系統級通知、行動裝置同步 |
| Hardcodet.NotifyIcon (WPF/WinForms) | 系統托盤整合、彈出自定義視窗 | 非原生 Toast、需自行動畫 | 背景常駐應用 |
// Program.cs 啟用高 DPI 感知 (.NET 4.7+)
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
// 多螢幕:針對游標所在螢幕顯示
Screen screen = Screen.FromPoint(Cursor.Position);
Rectangle wa = screen.WorkingArea;
Location = new Point(wa.Right - WidthMax - 20, wa.Bottom - HeightMax - 10);
AccessibleName/AccessibleDescription 給螢幕閱讀器。TabStop、Enter 觸發動作)。SystemInformation.MinimizeAnimation)。Timer + Height/Location 手動動畫 → 簡單、相容性好。Opacity 淡入淡出、ToastManager 佇列、高 DPI 支援。Storyboard / AnimationTimeline,分離視圖與邏輯 (MVVM)。ToastNotificationManager,整合系統動作中心、支援豐富互動 (按鈕、輸入、圖片)。這個經典範例展示了「在限制中創造體驗」的工程智慧:用最基礎的 Timer、Form 屬性、GDI+ 繪圖,構建出當時最前衛的通知體驗。今天雖有更強大的工具,但理解「無邊框視窗 + 置頂 + 手動定位 + 幀動畫」的核心模式,仍是自定義覆蓋層、螢幕標註工具、遊戲內覆蓋 (Overlay) 開發的基石。選擇技術棧時,請依據部署環境、目標框架、體驗要求三者平衡決策。