.NET Compact Framework (NET CF) 並未內建 NotifyIcon 控制項,這讓許多開發者在智慧型裝置上實作系統托盤圖示時感到棘手。本文將示範如何利用 Microsoft.WindowsCE.Forms.MessageWindow 類別搭配 Windows CE 平台 API Shell_NotifyIcon,自行打造一個功能完整的 NotifyIcon 類別。
Microsoft.WindowsCE.Forms 命名空間提供了 MessageWindow 類別,它擁有虛擬方法 WndProc 可攔截視窗訊息,並暴露原生視窗控制代碼 Hwnd。我們繼承此類別並覆寫 WndProc,即可捕捉托盤圖示的點擊事件(WM_LBUTTONDOWN)。
Windows CE SDK 包含 coredll.dll 中的 Shell_NotifyIcon 函式,簽名如下:
[DllImport("coredll.dll")]
internal static extern int Shell_NotifyIcon(int dwMessage, ref NOTIFYICONDATA pnid);
關鍵結構 NOTIFYICONDATA 定義包含圖示控制代碼、接收訊息的視窗控制代碼、回呼訊息 ID 等欄位。注意 pnid 必須以 ref 方式傳遞(指標語意)。
以下為 NotifyIcon 類別完整原始碼,支援多種圖示來源(檔案控制代碼、資源 ID、System.Drawing.Icon 物件)並提供新增、移除、修改圖示的方法。
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Drawing;
namespace NotifyClient
{
///
/// 智慧型裝置托盤圖示類別
///
public class NotifyIcon
{
// 點擊事件
public event EventHandler Click;
private MyMessageWindow messageWindow;
private int uID = 5000;
private Icon _icon;
public NotifyIcon()
{
messageWindow = new MyMessageWindow(this);
messageWindow.uID = uID;
}
public Icon Icon
{
set { _icon = value; }
}
~NotifyIcon()
{
Remove();
}
#region 公開方法:新增、移除、修改圖示
/// 使用圖示控制代碼新增托盤圖示
public void Add(IntPtr hIcon)
{
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint)uID, hIcon);
}
/// 使用編譯後資源名稱(如 "#201547")新增
public void Add(string iconRes)
{
IntPtr hIcon = LoadIcon(GetModuleHandle(null), iconRes);
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint)uID, hIcon);
}
/// 使用 Icon 物件新增
public void Add(Icon icon)
{
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint)uID, icon.Handle);
}
/// 使用屬性 Icon 新增
public void Add()
{
if (_icon != null)
NotifyMessage(messageWindow.Hwnd, NIM_ADD, (uint)uID, _icon.Handle);
}
/// 移除托盤圖示
public void Remove()
{
NotifyMessage(messageWindow.Hwnd, NIM_DELETE, (uint)uID, IntPtr.Zero);
}
/// 修改現有托盤圖示
public void Modify(IntPtr hIcon)
{
NotifyMessage(messageWindow.Hwnd, NIM_MODIFY, (uint)uID, hIcon);
}
#endregion
#region 核心私有方法與 P/Invoke 定義
private void NotifyMessage(IntPtr hwnd, int dwMessage, uint uID, IntPtr hIcon)
{
NOTIFYICONDATA notdata = new NOTIFYICONDATA();
notdata.cbSize = 152; // 結構大小
notdata.hIcon = hIcon;
notdata.hWnd = hwnd;
notdata.uCallbackMessage = WM_NOTIFY_TRAY;
notdata.uFlags = NIF_MESSAGE | NIF_ICON;
notdata.uID = uID;
Shell_NotifyIcon(dwMessage, ref notdata);
}
// API 常數
private const int NIF_MESSAGE = 0x00000001;
private const int NIF_ICON = 0x00000002;
private const int WM_LBUTTONDOWN = 0x0201;
private const int NIM_ADD = 0x00000000;
private const int NIM_MODIFY = 0x00000001;
private const int NIM_DELETE = 0x00000002;
private const int WM_NOTIFY_TRAY = 0x0400 + 2001; // 自訂訊息
[StructLayout(LayoutKind.Sequential)]
private struct NOTIFYICONDATA
{
public int cbSize;
public IntPtr hWnd;
public uint uID;
public uint uFlags;
public uint uCallbackMessage;
public IntPtr hIcon;
}
[DllImport("coredll.dll")]
private static extern int Shell_NotifyIcon(int dwMessage, ref NOTIFYICONDATA pnid);
[DllImport("coredll.dll")]
private static extern IntPtr LoadIcon(IntPtr hInst, string iconName);
[DllImport("coredll.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
#endregion
#region 內部訊息視窗類別
private class MyMessageWindow : Microsoft.WindowsCE.Forms.MessageWindow
{
private int _uID;
private NotifyIcon _notifyIcon;
public MyMessageWindow(NotifyIcon notifyIcon)
{
_notifyIcon = notifyIcon;
}
public int uID
{
set { _uID = value; }
}
protected override void WndProc(ref Microsoft.WindowsCE.Forms.Message msg)
{
if (msg.Msg == WM_NOTIFY_TRAY)
{
if ((int)msg.LParam == WM_LBUTTONDOWN && (int)msg.WParam == _uID)
{
_notifyIcon.Click?.Invoke(_notifyIcon, EventArgs.Empty);
}
}
}
}
#endregion
}
}
// 在 Form 建構式中初始化
NotifyIcon trayIcon = new NotifyIcon();
trayIcon.Icon = new Icon("app.ico"); // 或從資源載入
trayIcon.Click += (s, e) => MessageBox.Show("托盤圖示被點擊!");
trayIcon.Add(); // 顯示到托盤
Microsoft.WindowsCE.Forms.dll。