嵌入.NET控件到托盘图标Tooltip
介绍
本文将开发一个NotifyIcon控件,介绍如何为NotifyIcon的气球提示添加控件,例如添加一个超级连接,或一个日期控件到NotifyIcon的气球提示。如下图所示:
让我们看看如何来解决这个问题:
第一步
NofifyIcon是一个密封类,所以我们不能继承和覆盖ShowBalloonTip方法。解决办法就是我们使用一个类来封装NotifyIcon类,并实现我们自己的方法和属性。其中一些方法会被重定向到NotifyIcon的方法。这个类很简单,包含如下属性:
BalloonTipIcon
BalloonTipTitle
ContextMenu
ContextMenuStrip
Text
Icon
Visible
BalloonTipText
我们将重绘tooltip的内容区域,然后将一个Panel传递给InsidePanel类即可。在我们的封装类中,我们将定义如下事件:
NotifyClicked
NotifyDoubleClicked
NotifyMouseClicked
NotifyMouseUp
NotifyMouseDown
NotifyMouseMove
NotifyMouseDoubleClicked
第二步
我们需要一个NotifyIcon的HWND句柄。为达到这个目的,我们使用反射来获取 NotifyIcon的私有字段 "window" :
private IntPtr GetHandler(Object @object)
{
FieldInfo fieldInfo = @object.GetType().GetField("window",
BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);NativeWindow nativeWindow = (NativeWindow)fieldInfo.GetValue(@object);
if (nativeWindow.Handle == IntPtr.Zero)
return IntPtr.Zero;
return nativeWindow.Handle;
}
第三步
使用WINAPI的FindWindowEx查找"Shell_TrayWnd"类名的窗口托盘
IntPtr hWndTray = WINAPI.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Shell_TrayWnd", 0);
if (hWndTray == IntPtr.Zero)
return false;
然后使用EnumChildWindows,我们将会发现"ToolbarWindow32"类。
WINAPI.EnumChildWindowsCallback callback =
new WINAPI.EnumChildWindowsCallback(EnumChildWindowsFunc);...
WINAPI.EnumChildWindows(hWndTray, callback, 0);
if (!mFound)
return false;...
protected bool EnumChildWindowsFunc(IntPtr hwnd, IntPtr lParam)
{
StringBuilder sb = new StringBuilder(256);
WINAPI.GetClassName(hwnd, sb, sb.Capacity);
if (sb.ToString().StartsWith("ToolbarWindow32"))
{
mHWndNotify = hwnd;
mFound = true;
return false;
}
mFound = false;
return true;
}
最好,使用MapWindowPoint,我们会获取通知图片的矩形区域。
最后一步
最后,我们创建一个托管窗口的工具提示和气球。工具提示的内容将在其窗口过程重绘。
//Create parameters for a new tooltip window
System.Windows.Forms.CreateParams moCreateParams =
new System.Windows.Forms.CreateParams();// New window is a tooltip and a balloon
moCreateParams.ClassName = WINAPI.TOOLTIPS_CLASS;
moCreateParams.Style = WINAPI.WS_POPUP | WINAPI.TTS_NOPREFIX |
WINAPI.TTS_ALWAYSTIP | WINAPI.TTS_BALLOON;
moCreateParams.Parent = loNotifyIconHandle;// Create the tooltip window
moNativeWindow.CreateHandle(moCreateParams);//We save old window proc to be used later and replace it with our own
IntPtr loNativeProc = WINAPI.SetWindowLong(moNativeWindow.Handle,
WINAPI.GWL_WNDPROC, wpcallback);if (loNativeProc == IntPtr.Zero)
return;if (WINAPI.SetProp(moNativeWindow.Handle, "NATIVEPROC", loNativeProc) == 0)
return;
显示工具提示之前,还必须调整它的大小,使用我们的.net面板适合客户区的大小。我们这里使用GetTextExtentPoint32来获取字体的大小以调整我们tooltip区域需要的大小。
接着我们处理窗口过程,首先,我们调用原来的窗口过程,将出现提示。在处理WM_PAINT消息,我们将会得到tooltip的区域的大小。为了可以关闭tooltip我们在右上角增加一个.NET 按钮。
// 16 pixs for the button control and 2 pixels from top
moCloseButton.Location = new System.Drawing.Point
(width - (rect.left + 16), rect.top - 2);
moCloseButton.AutoSize = false;
moCloseButton.Click += CloseButtonClick;
下面是tooltip的关闭代码:
private void CloseButtonClick(object sender, EventArgs e)
{
CloseToolTip();
}private void CloseToolTip()
{
WINAPI.TOOLINFO ti = new WINAPI.TOOLINFO();
ti.cbSize = Marshal.SizeOf(ti.GetType());
ti.hwnd = GetHandler(moNotifyIcon);
WINAPI.SendMessage(moNativeWindow.Handle, WINAPI.TTM_DELTOOL, 0, ref ti);
...
}
最后,将要显示的面板添加到NotifyIcon的正确位置:
//Adding panel to the tooltip
moPanel.Location = new Point(rect.left, rect.top + 16);
WINAPI.SetParent(moPanel.Handle, hWnd);
使用方法
为窗体From类添加我们自定义控件的字段:
class Form1
{
private WControls.WNotifyIcon wNotifyIcon1;
}
显示tooltip的代码:
wNotifyIcon1.Visible = true;
TestPanel loPanel = new TestPanel();
wNotifyIcon1.InsidePanel = loPanel;
wNotifyIcon1.ShowBalloonTip(10000);