一、为什么选择这种实现方式?
- 用户体验升级:程序启动即隐藏,避免任务栏混乱,仅保留托盘图标交互。
- 轻量级方案:仅需原生控件(
NotifyIcon
+ContextMenuStrip
),无需第三方库。 - 高度可控:支持自定义菜单、图标、双击交互逻辑,适配各种业务场景。
二、核心代码详解与深度实现
2.1 项目初始化与控件配置
2.1.1 窗体属性设置
public partial class MainForm : Form
{
// 单例模式确保唯一实例
private static MainForm _instance;
public static MainForm Instance => _instance ?? (_instance = new MainForm());
// 托盘图标控件
private NotifyIcon _notifyIcon;
// 右键菜单
private ContextMenuStrip _contextMenu;
public MainForm()
{
InitializeComponent();
// 关键属性设置
this.ShowInTaskbar = false; // 不显示在任务栏
this.WindowState = FormWindowState.Minimized; // 启动即最小化
this.Opacity = 0; // 初始透明度为0,避免闪现
}
}
2.2 托盘图标与菜单初始化
private void InitializeTrayIcon()
{
// 1. 创建右键菜单
_contextMenu = new ContextMenu(); // 或 ContextMenuStrip
_contextMenu.MenuItems.Add("显示窗口", new EventHandler(ShowForm));
_contextMenu.MenuItems.Add("隐藏窗口", new EventHandler(HideForm));
_contextMenu.MenuItems.Add("-"); // 分隔符
_contextMenu.MenuItems.Add("退出程序", new EventHandler(ExitApp));
// 2. 创建NotifyIcon控件
_notifyIcon = new NotifyIcon()
{
Icon = Properties.Resources.AppIcon, // 嵌入资源图标,更可靠
Text = "我的程序", // 鼠标悬停提示
ContextMenu = _contextMenu,
Visible = true
};
// 3. 绑定双击事件
_notifyIcon.DoubleClick += (s, e) =>
{
ToggleFormVisibility(); // 双击切换显示/隐藏
};
}
2.3 窗体显示与隐藏逻辑
private void ToggleFormVisibility()
{
if (this.WindowState == FormWindowState.Minimized)
{
this.Show();
this.WindowState = FormWindowState.Normal;
this.Activate(); // 置顶窗体
}
else
{
this.Hide();
this.WindowState = FormWindowState.Minimized;
}
}
// 显示窗体事件
private void ShowForm(object sender, EventArgs e)
{
this.Show();
this.WindowState = FormWindowState.Normal;
this.BringToFront();
}
// 隐藏窗体事件
private void HideForm(object sender, EventArgs e)
{
this.Hide();
this.WindowState = FormWindowState.Minimized;
}
// 退出程序事件
private void ExitApp(object sender, EventArgs e)
{
if (MessageBox.Show("确定要退出程序吗?", "提示", MessageBoxButtons.OKCancel) == DialogResult.OK)
{
_notifyIcon.Visible = false; // 隐藏托盘图标
Application.Exit(); // 安全退出
}
}
2.4 窗体状态监听与异常处理
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// 防止用户通过任务栏恢复
this.ShowInTaskbar = false;
InitializeTrayIcon(); // 初始化托盘
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
e.Cancel = true; // 取消关闭,改为隐藏
this.Hide();
this.WindowState = FormWindowState.Minimized;
}
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MINIMIZE = 0xF020;
if (m.Msg == WM_SYSCOMMAND && (int)m.WParam == SC_MINIMIZE)
{
this.Hide(); // 拦截最小化操作,直接隐藏
return;
}
base.WndProc(ref m);
}
2.5 资源管理与性能优化
// 嵌入图标资源(推荐)
// 在项目资源管理器中右键添加"图标文件",设置生成操作为"嵌入的资源"
// 通过Properties.Resources访问
// 自动清理资源
private void MainForm_Disposed(object sender, EventArgs e)
{
_notifyIcon.Dispose();
_contextMenu.Dispose();
}
// 防止内存泄漏
protected override void Dispose(bool disposing)
{
if (disposing)
{
components?.Dispose();
_notifyIcon?.Dispose();
_contextMenu?.Dispose();
}
base.Dispose(disposing);
}
三、进阶功能扩展
3.1 自定义托盘图标动画
// 图标闪烁提示(如消息到达)
private void StartIconAnimation()
{
Timer animationTimer = new Timer();
animationTimer.Interval = 500; // 500ms切换
bool visible = true;
animationTimer.Tick += (s, e) =>
{
visible = !visible;
_notifyIcon.Visible = visible; // 开关图标可见性
};
animationTimer.Start();
// 3秒后停止
Task.Delay(3000).ContinueWith(t =>
{
animationTimer.Stop();
_notifyIcon.Visible = true; // 恢复显示
});
}
3.2 右键菜单动态更新
// 根据状态动态添加菜单项
private void UpdateContextMenu()
{
// 移除旧项
_contextMenu.MenuItems.Clear();
// 重新添加基础项
_contextMenu.MenuItems.Add("显示窗口", new EventHandler(ShowForm));
_contextMenu.MenuItems.Add("隐藏窗口", new EventHandler(HideForm));
_contextMenu.MenuItems.Add("-");
// 动态添加状态相关项
if (IsOnline())
{
_contextMenu.MenuItems.Add("断开连接", new EventHandler(Disconnect));
}
else
{
_contextMenu.MenuItems.Add("连接服务器", new EventHandler(Connect));
}
_notifyIcon.ContextMenu = _contextMenu; // 重新绑定
}
四、常见问题与解决方案
4.1 问题1:图标无法显示
原因:
- 图标路径错误
- 图标未正确嵌入为资源
解决方案:
// 推荐使用嵌入资源
public static Icon GetEmbeddedIcon(string resourceName)
{
using (Stream stream = Assembly.GetExecutingAssembly()
.GetManifestResourceStream(resourceName))
{
if (stream == null)
throw new FileNotFoundException($"资源{resourceName}未找到");
return new Icon(stream);
}
}
// 初始化时使用
_notifyIcon.Icon = GetEmbeddedIcon("YourNamespace.Resources.AppIcon.ico");
4.2 问题2:窗体多次实例化
解决方案:
// 程序入口单例化
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 检查是否已存在实例
if (IsAnotherInstanceRunning())
{
MessageBox.Show("程序已在运行");
return;
}
Application.Run(MainForm.Instance);
}
private static bool IsAnotherInstanceRunning()
{
var mutex = new System.Threading.Mutex(true, "Global\\MyAppMutex", out bool createdNew);
if (!createdNew)
{
mutex.ReleaseMutex();
return true;
}
return false;
}
}
五、性能优化与部署建议
5.1 启动速度优化
// 延迟初始化托盘图标
private bool _isTrayInitialized = false;
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
if (!_isTrayInitialized)
{
InitializeTrayIcon();
_isTrayInitialized = true;
}
}
5.2 部署注意事项
# 生成发布版本时:
1. 确保所有图标资源已嵌入
2. 配置manifest文件请求管理员权限(如需系统级操作)
3. 使用ILMerge合并第三方库(可选)
六、完整代码示例
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public sealed class MainForm : Form
{
private static MainForm _instance;
public static MainForm Instance => _instance ?? (_instance = new MainForm());
private NotifyIcon _notifyIcon;
private ContextMenu _contextMenu;
public MainForm()
{
InitializeComponent();
this.ShowInTaskbar = false;
this.WindowState = FormWindowState.Minimized;
this.Opacity = 0; // 隐藏启动闪屏
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
InitializeTrayIcon();
}
private void InitializeTrayIcon()
{
// 右键菜单
_contextMenu = new ContextMenu();
_contextMenu.MenuItems.Add("显示窗口", ShowForm);
_contextMenu.MenuItems.Add("隐藏窗口", HideForm);
_contextMenu.MenuItems.Add("-");
_contextMenu.MenuItems.Add("退出程序", ExitApp);
// 托盘图标
_notifyIcon = new NotifyIcon()
{
Icon = Properties.Resources.AppIcon,
Text = "我的程序",
ContextMenu = _contextMenu,
Visible = true
};
// 双击事件
_notifyIcon.DoubleClick += (s, e) => ToggleFormVisibility();
}
private void ToggleFormVisibility()
{
if (this.WindowState == FormWindowState.Minimized)
{
this.Show();
this.WindowState = FormWindowState.Normal;
this.Activate();
}
else
{
this.Hide();
this.WindowState = FormWindowState.Minimized;
}
}
private void ShowForm(object sender, EventArgs e) => ToggleFormVisibility();
private void HideForm(object sender, EventArgs e) => this.Hide();
private void ExitApp(object sender, EventArgs e)
{
if (MessageBox.Show("确定退出?", "提示", MessageBoxButtons.OKCancel) == DialogResult.OK)
{
_notifyIcon.Dispose();
Application.Exit();
}
}
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MINIMIZE = 0xF020;
if (m.Msg == WM_SYSCOMMAND && (int)m.WParam == SC_MINIMIZE)
{
this.Hide();
return;
}
base.WndProc(ref m);
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
e.Cancel = true;
this.Hide();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_notifyIcon?.Dispose();
_contextMenu?.Dispose();
}
base.Dispose(disposing);
}
}
// 程序入口
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// 单例检查
using (var mutex = new System.Threading.Mutex(true, "MyAppMutex", out bool createdNew))
{
if (!createdNew)
{
MessageBox.Show("程序已在运行");
return;
}
Application.Run(MainForm.Instance);
}
}
}