X

使用WinForm让WinUI程序托盘到右下角

XFEstudio 2026-02-01 16:28 148

WinUI 3(桌面) 里,系统托盘(右下角通知区域)不是官方内置功能,需要 自己用 Win32 API + NotifyIcon 来实现。好消息是:完全可行,而且是现在的主流做法


一、整体思路

WinUI 3 = UI 托盘 = Win32

做法:

  1. System.Windows.Forms.NotifyIcon 创建托盘图标
  2. 拦截窗口 最小化 / 关闭
  3. 隐藏 WinUI 窗口(而不是退出)
  4. 托盘右键菜单 → 显示 / 退出

二、准备工作(必须)

1️⃣ 引用 WinForms(只用 NotifyIcon)

项目文件 .csproj 中加:

<ItemGroup>
  <FrameworkReference Include="Microsoft.WindowsDesktop.App.WindowsForms" />
</ItemGroup>

然后在代码中可以用:

using System.Windows.Forms;

⚠️ 不会变成 WinForms 程序,只是借用托盘组件


三、创建托盘类(推荐单独封装)

TrayIconService.cs

using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.UI.Dispatching;

public class TrayIconService : IDisposable
{
    private readonly NotifyIcon _notifyIcon;
    private readonly DispatcherQueue _dispatcher;

    public TrayIconService(DispatcherQueue dispatcher)
    {
        _dispatcher = dispatcher;

        _notifyIcon = new NotifyIcon
        {
            Text = "我的 WinUI 程序",
            Icon = new Icon("Assets/app.ico"), // 一定要是 .ico
            Visible = true
        };

        var menu = new ContextMenuStrip();
        menu.Items.Add("显示", null, (_, _) => ShowWindow());
        menu.Items.Add("退出", null, (_, _) => ExitApp());

        _notifyIcon.ContextMenuStrip = menu;
        _notifyIcon.DoubleClick += (_, _) => ShowWindow();
    }

    private void ShowWindow()
    {
        _dispatcher.TryEnqueue(() =>
        {
            App.MainWindow.Show();
            App.MainWindow.Activate();
        });
    }

    private void ExitApp()
    {
        _notifyIcon.Visible = false;
        Environment.Exit(0);
    }

    public void Dispose()
    {
        _notifyIcon.Dispose();
    }
}

四、让 WinUI 窗口最小化到托盘

1️⃣ 在 App.xaml.cs 保存 MainWindow

public partial class App : Application
{
    public static MainWindow MainWindow { get; private set; }

    private TrayIconService _tray;

    protected override void OnLaunched(LaunchActivatedEventArgs args)
    {
        MainWindow = new MainWindow();
        MainWindow.Activate();

        _tray = new TrayIconService(
            DispatcherQueue.GetForCurrentThread()
        );
    }
}

2️⃣ 拦截“最小化”事件(关键)

MainWindow.xaml.cs

using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using WinRT.Interop;

public sealed partial class MainWindow : Window
{
    private AppWindow _appWindow;

    public MainWindow()
    {
        InitializeComponent();

        var hwnd = WindowNative.GetWindowHandle(this);
        _appWindow = AppWindow.GetFromWindowId(
            Win32Interop.GetWindowIdFromWindow(hwnd)
        );

        _appWindow.Closing += AppWindow_Closing;
    }

    private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
    {
        args.Cancel = true;           // 阻止真正关闭
        MainWindow.AppWindow.Hide();  // 隐藏到托盘
    }
}

👉 这样:

  • 点击 ❌ 不会退出
  • 程序仍在托盘中运行

五、最小化按钮 → 托盘(可选)

如果你想 点最小化也进托盘

_appWindow.Changed += (s, e) =>
{
    if (e.DidPresenterChange &&
        s.Presenter is OverlappedPresenter p &&
        p.State == OverlappedPresenterState.Minimized)
    {
        this.Hide();
    }
};

六、常见问题

❗1. 图标不显示

  • 必须是 .ico
  • 不能用 png / svg
  • 设置 “复制到输出目录”

❗2. 托盘点不了 / 崩溃

  • WinUI 是 STA
  • 所有 UI 操作 必须 DispatcherQueue

❗3. Alt+F4 直接退出

一定要拦截 AppWindow.Closing


七、效果总结

✔ WinUI 3 托盘常驻 ✔ 最小化到右下角 ✔ 双击恢复 ✔ 右键菜单 ✔ 完全原生 Windows 行为

0 条回复
暂无回复,快来抢沙发吧!
发表回复
登录 后发表回复。