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

WinForms 實作 MSN 風格滾動通知視窗:從 Timer 動畫到現代化 Toast 設計模式

     編輯:關於C語言

前言

經典 MSN Messenger 的右下角彈出通知視窗 (Toast Notification) 是桌面應用程式使用者體驗的里程碑。本文完整解析一個 WinForms 實作:無邊框視窗、Timer 驅動滑入/滑出動畫、螢幕工作區定位、最上層顯示、任務列隱藏,並延伸討論現代化替代方案 (WPF、WinUI 3、Windows 10/11 原生 Toast)。

一、核心架構:Form2 (通知視窗)

關鍵屬性設定


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();
}

二、進階強化:淡入淡出、點擊穿透、多視窗佇列

1. 淡入淡出 (Opacity 動畫)


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 同時執行,效果更自然。

2. 點擊通知觸發動作


private void ToastForm_Click(object sender, EventArgs e)
{
    // 開啟主視窗對應頁面
    MainForm.Instance.ShowConversation(messageId);
    Close(); // 立即關閉
}

3. 多通知佇列管理 (避免重疊)


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 + StoryboardGPU 加速、向量縮放、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、需自行動畫背景常駐應用

四、高 DPI 與多螢幕支援


// 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);

五、無障礙 (Accessibility) 考量

  • 提供 AccessibleName/AccessibleDescription 給螢幕閱讀器。
  • 支援鍵盤焦點 (TabStop、Enter 觸發動作)。
  • 尊重系統「減少動畫」設定 (SystemInformation.MinimizeAnimation)。

六、總結:從 Timer 到現代化的演進路徑

  1. 階段 1 (現狀):Timer + Height/Location 手動動畫 → 簡單、相容性好。
  2. 階段 2:引入 Opacity 淡入淡出、ToastManager 佇列、高 DPI 支援。
  3. 階段 3:遷移至 WPF Storyboard / AnimationTimeline,分離視圖與邏輯 (MVVM)。
  4. 階段 4:採用 Windows App SDK ToastNotificationManager,整合系統動作中心、支援豐富互動 (按鈕、輸入、圖片)。

結語

這個經典範例展示了「在限制中創造體驗」的工程智慧:用最基礎的 Timer、Form 屬性、GDI+ 繪圖,構建出當時最前衛的通知體驗。今天雖有更強大的工具,但理解「無邊框視窗 + 置頂 + 手動定位 + 幀動畫」的核心模式,仍是自定義覆蓋層、螢幕標註工具、遊戲內覆蓋 (Overlay) 開發的基石。選擇技術棧時,請依據部署環境、目標框架、體驗要求三者平衡決策。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved