Alex Yakhnin
IntelliProg, Inc.
2003年3月
适用于:
Microsoft® .NET Framework 精简版
Microsoft Visual Studio® .NET 2003
摘要:学习如何使用 .NET Framework 精简版中的 MessageWindow 类来创建 NotifyIcon。
下载 NotifyIcon.msi(英文)。(请注意,在示例文件中,程序员的注释使用的是英文,本文中将其译为中文是为了便于读者理解。)
简介
尽管 Microsoft® .NET Framework 精简版是一个丰富的 .NET Framework类库子集,但作为子集就意味着并非 .NET Framework 完整版中所有可用的类、方法和属性在 .NET Framework精简版中都可用。发送至托管控件的窗口消息的拦截就是这样一个例子。但 NET Framework 精简版实现了 MessageWindow 类,允许本地应用程序和托管应用程序通过 Windows 消息结构进行通信。
MessageWindow 是 Microsoft.WindowsCE.Forms 命名空间的一部分,因此必须在基于 .NET Framework 精简版的项目中添加对 Microsoft.WindowsCE.Forms.dll 程序集的引用。MessageWindow 类提供了 WndProc 方法,用于处理窗口消息并公开可能传递给本机窗口函数的有效窗口句柄。
要在程序中使用 MessageWindow,需要通过从 MessageWindow 派生来创建新类,并覆盖默认的 WndProc 行为,以便查看特定的窗口消息:
[C#
using Microsoft.WindowCE.Forms;
public class MyMessageWindow : MessageWindow
{
protected override void WndProc(ref Message msg)
{
switch(msg.Msg)
{
case WM_LBUTTONDOWN:
//在此处执行操作
break;
}
// 调用基类 WndProc 处理默认消息
base.WndProc(ref msg);
}
}
使 MessageWindow 开始工作
了解 MessageWindow 用法的最佳方法是使用示例。
我们将创建一个 NotifyIcon 对象,以便在 Today(今日)屏幕的工具栏上放置一个图标。该对象还将在用户单击图标时通知我们,以便我们执行相应的操作。
Window CE Platform API 包含 Shell_NotifyIcon 函数,用于向系统发送在工具栏状态区域中添加、修改或删除图标的消息。此函数的平台调用原型如下所示:
[DllImport("coredll.dll")]
internal static extern int Shell_NotifyIcon(int dwMessage, ref
NOTIFYICONDATA pnid);
其中
NOTIFYICONDATA 结构 被声明为:
struct NOTIFYICONDATA
{
int cbSize;
IntPtr hWnd;
uint uID;
uint uFlags;
uint uCallbackMessage;
IntPtr hIcon;
}
请注意,由于 Shell_NotifyIcon 需要一个指向 NOTIFYICONDATA 结构的指针,因此将 NOTIFYICONDATA pnid 参数声明为按引用传递。
NOTIFYICONDATA 结构中的 hWnd 成员是窗口(将接收与任务栏状态区域中的图标关联的通知消息)句柄。这就是 MessageWindow 的方便之处。
我们首先创建名为 NotifyClient 的新 Microsoft Visual C#™ Smart Device Application(智能设备应用程序),然后选择 Pocket PC 平台和 Windows 应用程序作为项目类型。将新的 NotifyIcon.cs 类添加到此项目中,并插入以下包装了对 Shell_NotifyIcon 函数调用的辅助函数:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace NotifyClient
{public class NotifyIcon
{private void TrayMessage(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);
}
}}
在上述代码中,我们创建了 NOTIFYICONDATA 结构的新实例,指定了所有必要的成员,并将该实例传递给 Shell_NotifyIcon 函数。
然后,将创建 MessageWindow 的子类,用于处理要从任务栏发送的消息。新类 WindowSink 将位于 NotifyIcon 的内部,因为我们不希望使用 NotifyIcon 类的客户端能够访问 MessageWindow 提供的消息循环:
internal class WindowSink : Microsoft.WindowsCE.Forms.MessageWindow
{
//私有成员
private int m_uID = 0;
private NotifyIcon notifyIcon;
//构造函数
public WindowSink(NotifyIcon notIcon)
{
notifyIcon = notIcon;
}
public int uID
{
set
{
m_uID = value;
}
}
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_NOTIFY_TRAY)
{
if((int)msg.LParam == WM_LBUTTONDOWN)
{
if ((int)msg.WParam == m_uID)
{
//如果某个用户被挂起,则引发事件
if (notifyIcon.Click != null)
notifyIcon.Click(notifyIcon, null);}
}
}}
}
在 WndProc 覆盖中,我们尝试捕获自定义消息 WM_NOTIFY_TRAY,以确定工具栏上的图标是否被点击。如果被点击,则引发 NotifyIcon 类的 Click 事件。需要在 NotifyIcon 类中按如下所示对 Click 事件进行声明:
public event System.EventHandler Click;
下一步,我们将添加这些公共方法:
public void Add(IntPtr hIcon)
{
TrayMessage(windowSink.Hwnd, NIM_ADD, (uint)uID, hIcon);
}
public void Remove()
{TrayMessage(windowSink.Hwnd, NIM_DELETE, (uint)uID, IntPtr.Zero);
}public void Modify(IntPtr hIcon)
{TrayMessage(windowSink.Hwnd, NIM_MODIFY, (uint)uID, hIcon);
}
重要信息:应用程序退出时,必须从工具栏删除此图标。这就是我们应该在 NotifyIcon 类的析构函数中添加清除代码的原因:
//析构函数
~NotifyIcon()
{
Remove();
}
所有必要的代码都必须提供所需功能。
请记住,本文所列举的只是部分代码。可以下载整个代码示例,请参阅本文开头的链接。
生成客户端
现在,即可使用创建的 NotifyIcon 类。首先,我们将向创建的 NotifyClient 项目中添加应用程序图标。要完成此操作,需要从 Visual Studio 主菜单中选择 Project(项目)- Properties(属性)来打开 Project Properties Dialog(项目属性)对话框。选择 Application Icon(应用程序图标)项,并指定您选择的图标文件。
图 1:Project Properties(项目属性)对话框
编译程序时,我们选择的图标将成为本机资源,可以使用 LoadIcon 平台 API 调用进行检索:
[DllImport("coredll.dll")]
internal static extern IntPtr LoadIcon(IntPtr hInst, string IconName);
此函数中的第二个参数是已编译程序中的应用程序图标的资源标识符。通过使用资源管理器在已编译的可执行文件中拾取,我发现基于 .NET Framework 精简版的应用程序图标与本机应用程序“#32512”的资源标识符相同。因此,现在我们已拥有调用 NotifyIcon 类所需的全部信息。让我们在表单上对齐两个按钮,并在其 click 事件中插入以下代码:
private void cmdAddIcon_Click(object sender, System.EventArgs e)
{
IntPtr hIcon = LoadIcon(GetModuleHandle(null), "#32512");
notifyIcon.Add(hIcon);
}
private void cmdRemoveIcon_Click(object sender, System.EventArgs e)
{
notifyIcon.Remove();
}
不要忘记连接到 NotifyIcon 的 Click 事件,以便在用户点击图标时获得通知:
public Form1()
{
//
// Windows Form Designer(Windows 窗体设计器)支持所需
//
InitializeComponent();
// 创建实例
notifyIcon = new NotifyIcon();
// 连接到事件
notifyIcon.Click+=new EventHandler(notifyIcon_Click);
}
private void notifyIcon_Click(object sender, EventArgs e)
{
MessageBox.Show("图标被单击!");
}
因此,运行程序后,点击 Add Icon(添加图标)按钮并切换到 Pocket PC 上的 Today(今日)视图,应该可以看到所创建的图标:
图 2:Today(今日)屏幕
点击图标将立即引发 notifyIcon_Click 事件并显示一个消息框:
图 3:已捕获的事件
小结
我们刚刚完成了 NotifyIcon 类的创建,即通过 P/Invoke 使用 Windows CE API,以及将 MessageWindow 类用作接收与工具栏上的图标关联的通知消息的窗口。