嵌入.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);
 

代码下载

WNotifyIconTest.zip

posted @ 2011-08-12 18:19  敏捷学院  阅读(1351)  评论(0编辑  收藏  举报