C#托盘程序开发完全指南


引言

托盘程序(系统托盘应用程序)是指主要在Windows通知区域(系统托盘)运行的后台应用程序,它们通常不显示主窗口或最小化到托盘。这类程序广泛用于监控工具、即时通讯软件、系统实用程序等场景。本文将全面介绍使用C#开发托盘程序的各种技术细节和最佳实践。

一、基础托盘程序实现

1. 创建NotifyIcon控件

private NotifyIcon trayIcon;

public MainForm()
{
    // 初始化托盘图标
    trayIcon = new NotifyIcon
    {
        Icon = SystemIcons.Application, // 设置图标
        Text = "我的托盘程序",        // 鼠标悬停提示文本
        Visible = true                // 必须设置为true
    };

    // 添加上下文菜单
    trayIcon.ContextMenuStrip = CreateContextMenu();

    // 双击图标事件
    trayIcon.DoubleClick += (s, e) => ShowMainWindow();

    // 窗口关闭时处理
    this.FormClosing += MainForm_FormClosing;
}

2. 最小化到托盘而非关闭

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.UserClosing)
    {
        e.Cancel = true;        // 取消关闭
        this.Hide();            // 隐藏窗口
        ShowNotification("程序已最小化到托盘"); // 可选通知
    }
}

private void ShowMainWindow()
{
    this.Show();
    this.WindowState = FormWindowState.Normal;
    this.Activate();
}

二、托盘程序核心功能实现

1. 创建上下文菜单

private ContextMenuStrip CreateContextMenu()
{
    var menu = new ContextMenuStrip();

    // 添加菜单项
    menu.Items.Add("显示主窗口", null, (s, e) => ShowMainWindow());
    menu.Items.Add("设置", null, (s, e) => OpenSettings());
    menu.Items.Add(new ToolStripSeparator());
    menu.Items.Add("退出", null, (s, e) => ExitApplication());

    return menu;
}

2. 显示气泡通知

private void ShowNotification(string message, string title = "提示", ToolTipIcon icon = ToolTipIcon.Info)
{
    trayIcon.ShowBalloonTip(
        3000,       // 显示时间(毫秒)
        title,      // 标题
        message,    // 消息内容
        icon        // 图标类型
    );

    // 处理气球通知点击
    trayIcon.BalloonTipClicked += (s, e) => ShowMainWindow();
}

三、高级功能实现

1. 单实例运行控制

static class Program
{
    private static Mutex mutex;

    [STAThread]
    static void Main()
    {
        bool createdNew;
        mutex = new Mutex(true, "MyTrayAppUniqueMutexName", out createdNew);

        if (!createdNew)
        {
            MessageBox.Show("程序已经在运行中");
            return;
        }

        Application.Run(new MainForm());
        GC.KeepAlive(mutex); // 保持mutex不被GC回收
    }
}

2. 开机自启动实现

public static void SetStartup(bool enable)
{
    RegistryKey rk = Registry.CurrentUser.OpenSubKey(
        "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);

    if (enable)
    {
        rk.SetValue("MyTrayApp", Application.ExecutablePath);
    }
    else
    {
        rk.DeleteValue("MyTrayApp", false);
    }
}

四、现代化托盘程序特性

1. 使用现代通知(Windows 10+)

// 需要引用Windows API Code Pack
private void ShowToastNotification(string title, string message)
{
    var toast = new ToastNotification
    {
        Title = title,
        Body = message,
        Duration = ToastDuration.Short,
        Audio = new ToastAudio { Content = ToastAudioContent.Default }
    };

    toast.Activated += (s, e) => ShowMainWindow();

    ToastNotificationManager.CreateToastNotifier("MyTrayApp").Show(toast);
}

2. 动态图标变更

// 更新托盘图标
public void UpdateTrayIcon(Icon newIcon, string newTooltip = null)
{
    trayIcon.Icon = newIcon;
    if (newTooltip != null)
    {
        trayIcon.Text = newTooltip;
    }
}

// 创建动态图标(带数字)
public Icon CreateNumberedIcon(int number)
{
    Bitmap bmp = new Bitmap(16, 16);
    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.Clear(Color.Transparent);
        g.DrawString(number.ToString(), 
                     new Font("Arial", 8), 
                     Brushes.White, 
                     new PointF(0, 0));
    }
    return Icon.FromHandle(bmp.GetHicon());
}

五、最佳实践与注意事项

  1. 资源释放:确保退出时释放托盘图标
   private void ExitApplication()
   {
       trayIcon.Visible = false;
       trayIcon.Dispose();
       Application.Exit();
   }
  1. 用户友好设计
  • 提供明显的退出途径
  • 避免过多的气球通知打扰用户
  • 图标设计简洁明了
  1. 异常处理
   private void TrayIcon_MouseClick(object sender, MouseEventArgs e)
   {
       try
       {
           // 处理代码
       }
       catch (Exception ex)
       {
           ShowNotification($"操作失败: {ex.Message}", "错误", ToolTipIcon.Error);
       }
   }
  1. 多线程考虑
  • 使用Invoke/BeginInvoke更新UI
  • 后台任务使用Task或BackgroundWorker

六、跨平台考虑(.NET Core/.NET 5+)

对于跨平台托盘程序,可以考虑以下方案:

  1. 使用TrayIcon库
  • Hardcodet.NotifyIcon.Wpf (WPF)
  • TrayIcon (Avalonia)
  1. Electron.NET:结合Web技术开发跨平台托盘应用
  2. MAUI:.NET的多平台应用UI框架

结语

C#开发托盘程序既可以利用传统的WinForms NotifyIcon控件快速实现基本功能,也可以通过集成现代Windows API实现更丰富的用户体验。良好的托盘程序应该做到”存在但不打扰”,在提供必要功能的同时保持低调。开发时务必注意资源管理、异常处理和用户交互设计,确保程序的稳定性和易用性。


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注