65.9K
CodeProject 正在变化。 阅读更多。
Home

在运行时创建图标并在系统托盘中显示它们

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.66/5 (16投票s)

2007 年 11 月 28 日

CPOL

4分钟阅读

viewsIcon

79094

downloadIcon

2244

在运行时创建动态图标并在系统托盘中显示它们

引言

我使用一个工具通过 RS232 监测来自不同温度计的两个测量值。在监测期间,当我使用计算机上的其他应用程序时,我无法在对话框窗口中看到测量值,因为该工具被应用程序窗口覆盖了。因此,我需要一个独立于桌面状态的显示。我决定将值写入图标并在系统托盘中重复显示这些图标。

DynIcon

在阅读了关于系统托盘中图标的其他文章后,您可能会问这个演示有什么特别之处?这个演示的主要看点在于能够以简单的方式在运行时动态创建图标,并用这些图标永久更新系统托盘。

两个版本

代码有两个版本。我第一次提交文章时附带了 MFC 演示项目并解释了代码。六个月后,我用 C# 版本更新了文章。因为 C# 代码更容易理解,所以我保留了 MFC 代码的解释不变,只添加了 C# 演示项目。在这个新项目中,您会发现几乎所有的函数和变量都与 MFC 代码中的一样,因为原理没有改变。

Using the Code

该工具的工作方式如下:程序启动时,一个静态(默认)图标会出现在系统托盘中。只有当您按下“开始”按钮时,才会有两个显示变化值的图标。您可以通过最小化按钮隐藏对话框。程序将从任务栏消失,只有变化中的图标会显示在系统托盘中。双击图标,对话框会弹出到桌面(如果对话框可见,则从桌面隐藏)。

在演示项目中,我使用一个计数器来模拟测量值的动态变化。这是通过一个定时器实现的。在每次定时器事件 TIMER1 中,值会改变,图标会被绘制然后推送到系统托盘。

//CDynIconDlg::OnTimer(UINT_PTR nIDEvent)
switch(nIDEvent){
    case TIMER1:
        ChangeValues();
        CreateIcons();
        PushIcons();
        break;

    default: break;
}

如何创建图标?

在这个演示中,我展示了如何从 IDE 的资源视图加载和显示一个静态图标,以及如何在运行时绘制一个图标。我使用一个静态图标作为默认图标,该图标已在资源视图中添加和设计。我只使用了 16x16 像素的图像类型。32x32 像素的图像类型可以删除,或者您可以使用相同的图标作为 IDR_MAINFRAME 图标。资源名称是 IDI_ICON1。在将图标推送到系统托盘的 NOTIFYCONDATA 结构中需要此名称(见下文)。要在运行时加载默认图标,我使用了以下代码:

//CDynIconDlg::SetDefaultIconInTray()
//create a handle for the default icon
HICON hIcon;

//load the icon from the resources
HINSTANCE hInst = 
    AfxFindResourceHandle(MAKEINTRESOURCE(IDI_ICON1),RT_GROUP_ICON);

hIcon = (HICON)LoadImage(hInst,MAKEINTRESOURCE(IDI_ICON1),
        IMAGE_ICON,16,16,LR_DEFAULTCOLOR);

//push the icon into the system tray
    .
    .
    .

在运行时创建图标

因为使用了 GDI+ 图形对象,所以我实现了 GDI+。如何实现它已在文章《Starting with GDI+.》中进行了说明。

这发生在 CreateIcon1()CreateIcon2() 中。
首先,我们必须准备要写入图标的值。对于使用的 DrawString() 命令,我们需要一个 widechar 数组。我们可以直接将值写入 widechar 数组 strValue...

//CDynIconDlg::CreateIcon1()
//we need a widechar array for the value
wchar_t strValue[4];

// because the wchar_t requires two bytes
int size = sizeof(strValue)/sizeof(wchar_t);

//the prefix L means, that the string should be a widechar
swprintf( strValue, size, L"%3d", m_nValue1 );

... 或者我们将值写入一个 char 数组并将其转换为 widechar 数组。

//CDynIconDlg::CreateIcon1()
char txt[4];
wchar_t strValue[4];
// because the wchar_t requires two bytes
int size = sizeof(strValue)/sizeof(wchar_t);

//write the value1 in the string
sprintf_s(txt, sizeof(txt),"%3d", m_nValue1);

//change to widechar
MultiByteToWideChar(CP_ACP, 0, txt, -1, strValue, size);

下一步,我们提供一个 Pen、一个 SolidBrush、一个 Font 和一些格式化内容。

//CDynIconDlg::CreateIcon1()
//you can change the colors depending on the value
//here it should be white only
//white pen to draw some lines in the icon
Pen whitePen(Color(255, 255, 255));

//white brush for the text
SolidBrush whiteBrush(Color(255,255,255));

//create a small font to write the values in the bitmap
Font font(L"Tahoma",8);

//origin for the string
PointF origin(-1, 1);

//alignment for the string
StringFormat format;
format.SetAlignment(StringAlignmentNear);

现在开始绘画。

//CDynIconDlg::CreateIcon1()
//create a new bitmap with 16x16 pixel
Bitmap bitmap(16,16);

//use the bitmap to draw
Graphics *graph = Graphics::FromImage(&bitmap);

//draw two horizontal lines (just for the show)
graph->DrawLine(&whitePen, 0, 15, 15, 15);
graph->DrawLine(&whitePen, 0,  0, 15,  0);

//draw the string including the value
//here we need the widechar array
graph->DrawString(strValue , -1, &font, origin, &format, &whiteBrush);

最后,我们将位图转换为图标并将其保存到我们的图标句柄 m_hIcon1。这个句柄是我们类的成员,因为我们稍后在 PushIcon1() 中需要它来将其推送到系统托盘。

//CDynIconDlg::CreateIcon1()
bitmap.GetHICON(&m_hIcon1);

将图标推送到系统托盘

首先,我们需要一个 NOTIFYCONDATA 结构,其中填充了一些数据,包括名称 ICON_VALUE1、用于双击图标的用户定义消息 WM_TRAY 以及创建图标的句柄 m_hIcon1

//CDynIconDlg::PushIcon1()
// set NOTIFYCONDATA structure
NOTIFYICONDATA tnid;

tnid.cbSize = sizeof(NOTIFYICONDATA);
tnid.hWnd = m_hWnd;
tnid.uID = ICON_VALUE1; //Resourcename

tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
tnid.uCallbackMessage = WM_TRAY;//user message

tnid.hIcon = m_hIcon1; //handle of the created icon

请注意资源名称。有三个不同的名称。静态默认图标的名称是 IDI_ICON1,而另外两个图标具有自定义名称 ICON_VALUE1ICON_VALUE2(参见 DynIconDlg.h)。

可以使用工具提示。每次值更改后,字符串 m_strTooltip 都会更新。在此演示中,我为两个图标使用相同的工具提示,并将字符串用作成员变量。

//CDynIconDlg::PushIcon1()
//copy the string to the NOTIFYCONDATA structure
lstrcpyn(tnid.szTip, m_strTooltip, sizeof(tnid.szTip));

现在我们需要检查图标是否是第一次出现。如果是,我们就使用带有 NIM_ADD 参数的 Shell_NotifyIcon,否则我们只使用 NIM_MODIFY 参数来更新图标。

//CDynIconDlg::PushIcon1()
if(m_bFirstIcon1){

    //for the first time we have to use the Shell_NotifyIcon with NIM_ADD
    //to add the icon to the tray
    Shell_NotifyIcon(NIM_ADD, &tnid);
    m_bFirstIcon1 = FALSE;
}
else{
    //the icon already exists
    //Shell_NotifyIcon with NIM_MODIFY
    Shell_NotifyIcon(NIM_MODIFY, &tnid);
}

现在图标已在系统托盘中,我们进行清理。

//CDynIconDlg::PushIcon1()
//frees the memory
DestroyIcon(m_hIcon1);

对第二个图标在 CreateIcon2()PushIcon2() 中也遵循相同的过程。

注释

代码中的其他函数都已注释。因此,我的解释很简短。我知道代码的编写效率不高。但我认为这有助于加深对所用函数的理解。

历史

  • 2007 年 11 月 29 日 - 创建文章
  • 2008 年 6 月 10 日 - 更新文章,添加 C# 代码
© . All rights reserved.