引言
托盘程序(系统托盘应用程序)是指主要在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());
}
五、最佳实践与注意事项
- 资源释放:确保退出时释放托盘图标
private void ExitApplication()
{
trayIcon.Visible = false;
trayIcon.Dispose();
Application.Exit();
}
- 用户友好设计:
- 提供明显的退出途径
- 避免过多的气球通知打扰用户
- 图标设计简洁明了
- 异常处理:
private void TrayIcon_MouseClick(object sender, MouseEventArgs e)
{
try
{
// 处理代码
}
catch (Exception ex)
{
ShowNotification($"操作失败: {ex.Message}", "错误", ToolTipIcon.Error);
}
}
- 多线程考虑:
- 使用Invoke/BeginInvoke更新UI
- 后台任务使用Task或BackgroundWorker
六、跨平台考虑(.NET Core/.NET 5+)
对于跨平台托盘程序,可以考虑以下方案:
- 使用TrayIcon库:
Hardcodet.NotifyIcon.Wpf
(WPF)TrayIcon
(Avalonia)
- Electron.NET:结合Web技术开发跨平台托盘应用
- MAUI:.NET的多平台应用UI框架
结语
C#开发托盘程序既可以利用传统的WinForms NotifyIcon控件快速实现基本功能,也可以通过集成现代Windows API实现更丰富的用户体验。良好的托盘程序应该做到”存在但不打扰”,在提供必要功能的同时保持低调。开发时务必注意资源管理、异常处理和用户交互设计,确保程序的稳定性和易用性。