本节将讲述工具栏控件的创建和使用。
本节必须掌握的知识点:
工具栏控件
第72练:工具栏控件
11.3.1 工具栏控件
工具栏控件(Toolbar Control)是Windows操作系统提供的一种用户界面元素,用于显示常用的命令按钮、工具图标和下拉菜单,以便用户快速访问和执行特定的操作。
工具栏通常位于应用程序窗口的顶部或边缘,并由一系列按钮和其他控件组成。用户可以单击工具栏上的按钮来触发相应的命令或操作。
在Windows编程中,工具栏控件由TOOLBARCLASSNAME类名标识,并使用CreateWindowEx或CreateWindow函数创建。
■以下是创建和设置工具栏控件的一般步骤:
1.定义和初始化工具栏按钮信息:创建一个TBBUTTON结构体数组,每个结构体表示一个工具栏按钮。可以指定按钮的标识符、样式、图标等属性。
2.创建工具栏控件:使用CreateWindowEx或CreateWindow函数创建工具栏控件,并指定类名为TOOLBARCLASSNAME。
3.设置工具栏样式和按钮信息:使用工具栏控件的消息,如TB_BUTTONSTRUCTSIZE、TB_ADDBUTTONS、TB_SETBITMAPSIZE等,来设置工具栏的样式和按钮信息。
4.处理工具栏按钮的消息:通过处理工具栏按钮的消息,如WM_COMMAND,来响应用户的点击操作,并执行相应的命令或操作。
■以下是一个使用工具栏控件的简单示例:
//创建工具栏
HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT, 0, 0, 0, 0,
hWndParent, NULL, hInstance, NULL);
// 设置按钮信息
TBBUTTON tbButtons[3];
ZeroMemory(tbButtons, sizeof(tbButtons));
tbButtons[0].iBitmap = 0;
tbButtons[0].idCommand = ID_BUTTON1;
tbButtons[0].fsState = TBSTATE_ENABLED;
tbButtons[0].fsStyle = TBSTYLE_BUTTON;
tbButtons[1].iBitmap = 1;
tbButtons[1].idCommand = ID_BUTTON2;
tbButtons[1].fsState = TBSTATE_ENABLED;
tbButtons[1].fsStyle = TBSTYLE_BUTTON;
tbButtons[2].iBitmap = 2;
tbButtons[2].idCommand = ID_BUTTON3;
tbButtons[2].fsState = TBSTATE_ENABLED;
tbButtons[2].fsStyle = TBSTYLE_BUTTON;
// 设置按钮尺寸
SendMessage(hWndToolbar, TB_SETBITMAPSIZE, 0, MAKELPARAM(16, 16));
// 添加按钮到工具栏
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0);
SendMessage(hWndToolbar, TB_ADDBUTTONS, sizeof(tbButtons) / sizeof(TBBUTTON), (LPARAM)tbButtons);
在上述示例中,使用CreateWindowEx函数创建了一个工具栏控件,并设置了一些基本的样式。然后,通过设置按钮信息、按钮尺寸和添加按钮到工具栏,完成了工具栏的初始化。
以上只是一个简单的示例,实际上可以根据需要对工具栏进行更多的设置和定制,如添加分隔符、下拉菜单、工具提示等。
■工具栏控件通知信息的处理
方法一:使用NMTOOLBAR消息
NMTOOLBAR 是一种通知消息(notification message),用于在工具栏控件(Toolbar Control)中传递通知信息。
当工具栏控件中的按钮被点击或其他状态改变时,工具栏控件会发送 NMTOOLBAR 消息给其父窗口,以便通知父窗口相应的事件和状态变化。
NMTOOLBAR 消息的参数是一个 NMHDR 结构体,其中包含了关于通知消息的详细信息,包括发送消息的控件句柄、标识符和消息代码。对于 NMTOOLBAR 消息,NMHDR 结构体的 code 域会被设置为 TBN_* 常量之一,表示不同的通知类型。
以下是一些常见的 NMTOOLBAR 消息和对应的 code 值:
●TBN_BEGINDRAG:当用户开始拖动工具栏按钮时发送的消息。
●TBN_ENDDRAG:当用户结束拖动工具栏按钮时发送的消息。
●TBN_BEGINADJUST:当用户开始自定义工具栏按钮时发送的消息。
●TBN_ENDEDIT:当用户结束自定义工具栏按钮时发送的消息。
●TBN_GETBUTTONINFO:当需要获取工具栏按钮的信息时发送的消息。
●TBN_DROPDOWN:当用户点击工具栏按钮上的下拉箭头时发送的消息。
父窗口可以通过处理 NMTOOLBAR 消息来响应工具栏控件的事件和状态改变。处理 NMTOOLBAR 消息的一种常见方式是使用 switch 语句来根据 code 值进行分支处理,以执行相应的操作。
以下是一个处理 NMTOOLBAR 消息的示例:
case WM_NOTIFY:
{
NMHDR* pnmhdr = (NMHDR*)lParam;
// 判断消息来源是工具栏控件
if (pnmhdr->hwndFrom == hWndToolbar)
{
// 判断通知消息的类型(code 值)
switch (pnmhdr->code)
{
case TBN_BEGINDRAG:
// 工具栏按钮开始拖动的处理
// ...
break;
case TBN_ENDDRAG:
// 工具栏按钮结束拖动的处理
// ...
break;
case TBN_DROPDOWN:
// 工具栏按钮下拉菜单的处理
// ...
break;
// 其他工具栏通知的处理...
default:
break;
}
}
break;
}
以上示例中,通过判断 NMTOOLBAR 消息的来源是工具栏控件,并根据不同的 code 值进行分支处理,来执行相应的操作。
●方法二:使用LPNMTTDISPINFO结构指针
LPNMTTDISPINFO 是一个指向 NMTTDISPINFO 结构体的指针类型。
NMTTDISPINFO 结构体是 ToolTip 控件(工具提示控件)发送的通知消息 TTN_GETDISPINFO 的参数,用于提供工具提示的显示信息。
NMTTDISPINFO 结构体定义如下:
typedef struct tagNMTTDISPINFO {
NMHDR hdr; // 通知消息的头部信息
LPTSTR lpszText; // 工具提示的文本
TCHAR szText[MAX_TIPTEXT]; // 工具提示的文本(兼容旧版本)
HINSTANCE hinst; // 包含工具提示文本的模块实例句柄
UINT uFlags; // 工具提示的标志
LPARAM lParam; // 消息特定的额外参数
#if (_WIN32_IE >= 0x0300)
int iIcon; // 额外的图标索引
#endif
} NMTTDISPINFO, *LPNMTTDISPINFO;
在 TTN_GETDISPINFO 通知消息中,ToolTip 控件会发送一个 NMTTDISPINFO 结构体给父窗口,父窗口可以使用该结构体来提供工具提示的显示信息。
在接收到 TTN_GETDISPINFO 通知消息时,父窗口可以修改 NMTTDISPINFO 结构体的成员,以提供自定义的工具提示文本、图标等信息。通过修改 lpszText 或 szText 成员,可以设置工具提示控件要显示的文本内容。
以下是一个处理 TTN_GETDISPINFO 消息的示例:
case WM_NOTIFY:
{
NMHDR* pnmhdr = (NMHDR*)lParam;
// 判断消息来源是工具提示控件
if (pnmhdr->hwndFrom == hWndTooltip &&
pnmhdr->code == TTN_GETDISPINFO)
{
LPNMTTDISPINFO pDispInfo = (LPNMTTDISPINFO)lParam;
// 修改工具提示的文本
pDispInfo->lpszText = _T("This is a tooltip.");
// 如果需要设置额外的图标索引
// pDispInfo->iIcon = iconIndex;
}
break;
}
在上述示例中,当收到 TTN_GETDISPINFO 消息,并且消息来源是工具提示控件时,通过修改 LPNMTTDISPINFO 结构体的 lpszText 成员,设置了工具提示的文本内容为 "This is a tooltip."。
●方法三:使用LPTOOLTIPTEXT结构指针
LPTOOLTIPTEXT 是一个指向 TOOLTIPTEXT 结构体的指针类型。
TOOLTIPTEXT 结构体是 ToolTip 控件(工具提示控件)发送的通知消息 TTN_NEEDTEXT 的参数,用于提供工具提示的显示信息。
TOOLTIPTEXT 结构体定义如下:
typedef struct tagTOOLTIPTEXT {
NMHDR hdr; // 通知消息的头部信息
LPTSTR lpszText; // 工具提示的文本
#if (_WIN32_IE >= 0x0300)
TCHAR szText[MAX_TIPTEXT]; // 工具提示的文本(兼容旧版本)
#endif
HINSTANCE hinst; // 包含工具提示文本的模块实例句柄
UINT uFlags; // 工具提示的标志
LPARAM lParam; // 消息特定的额外参数
} TOOLTIPTEXT, *LPTOOLTIPTEXT;
在 TTN_NEEDTEXT 通知消息中,ToolTip 控件会发送一个 TOOLTIPTEXT 结构体给父窗口,父窗口可以使用该结构体来提供工具提示的显示信息。
在接收到 TTN_NEEDTEXT 通知消息时,父窗口可以修改 TOOLTIPTEXT 结构体的成员,以提供自定义的工具提示文本。
以下是一个处理 TTN_NEEDTEXT 消息的示例:
case WM_NOTIFY:
{
NMHDR* pnmhdr = (NMHDR*)lParam;
// 判断消息来源是工具提示控件
if (pnmhdr->hwndFrom == hWndTooltip && pnmhdr->code == TTN_NEEDTEXT)
{
LPTOOLTIPTEXT pTooltipText = (LPTOOLTIPTEXT)lParam;
// 修改工具提示的文本
pTooltipText->lpszText = _T("This is a tooltip.");
}
break;
}
在上述示例中,当收到 TTN_NEEDTEXT 消息,并且消息来源是工具提示控件时,通过修改 LPTOOLTIPTEXT 结构体的 lpszText 成员,设置了工具提示的文本内容为 "This is a tooltip."。
11.3.2 第72练:工具栏控件
/*------------------------------------------------------------------------
072 WIN32 API 每日一练
第72个例子Toolbar.c:通用控件-工具栏控件
TBBUTTON结构
NMTOOLBAR结构
NMTTDISPINFO结构
NMHDR结构结构
WM_NOTIFY消息
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <Windows.h>
#include <commctrl.h>
#pragma comment(lib,"comctl32.lib")
#include "resource.h"
#define ID_TOOLBAR 1 //工具栏控件标识符
#define ID_EDIT 2 //编辑控件标识符
#define NUM_BUTTONS 16 //按钮数量
HWND hWinMain;
HWND hWinEdit, hWinToolbar;
//定义工具栏按钮
TBBUTTON tbb[16] = {
{STD_FILENEW,IDM_NEW,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{STD_FILEOPEN,IDM_OPEN,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{STD_FILESAVE,IDM_SAVE,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0,-1},
{STD_PRINTPRE,IDM_PAGESETUP,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{STD_PRINT,IDM_PRINT,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0,-1},
{STD_COPY,IDM_COPY,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{STD_CUT,IDM_CUT,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{STD_PASTE,IDM_PASTE,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0,-1},
{STD_FIND,IDM_FIND,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{STD_REPLACE,IDM_REPLACE,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0,-1},
{STD_HELP,IDM_HELP,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0,-1},
{0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0,-1}
};
void _Resize();//调整窗口大小
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);//窗口过程
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nShowCmd)//默认填写
{
static TCHAR szClassName[] = TEXT("ToolbarExample"); // 类名
static TCHAR szCaptionMain[] = TEXT("工具栏示例"); // 窗口窗口标题
…(略)
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
static TCHAR szClass[] = TEXT("EDIT"); // 类名
static TCHAR szCaption[] = TEXT("命令消息"); // 窗口窗口标题
static TCHAR szFormat[] = TEXT("收到 WM_COMMAND 消息,命令ID:%d");
static TCHAR szBuffer[128];
static NMTOOLBAR NMToolbar;//用于在工具栏控件中传递通知信息。
LPNMTTDISPINFO lpnmt; //该结构取代了 TOOLTIPTEXT结构
LPTOOLTIPTEXT lpToolTipText;
//TOOLINFO tbToolInfo; //该TOOLINFO结构包含在工具提示控制工具的信息
switch (message)
{
case WM_CREATE:
hWinMain = hwnd;
//创建编辑控件,窗口的边框具有凹陷边缘
hWinEdit = CreateWindowEx(WS_EX_CLIENTEDGE,szClass, NULL,
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | WS_VSCROLL | ES_AUTOHSCROLL,
0, 0, 0, 0, hwnd, (HMENU)ID_EDIT,GetModuleHandle(0), NULL);
//创建工具栏控件,使用系统预定义位图
//注意此函数不支持工具栏的所有功能。请改用CreateWindowEx。
hWinToolbar = CreateToolbarEx(hWinMain,WS_VISIBLE | WS_CHILD |TBSTYLE_FLAT |TBSTYLE_TOOLTIPS | CCS_ADJUSTABLE, //平面,提示信息,可拖动
ID_TOOLBAR,0,HINST_COMMCTRL,//包含资源模块的句柄
IDB_STD_SMALL_COLOR,//小型彩色位图
tbb,NUM_BUTTONS,0,0,0,0,sizeof(TBBUTTON));
_Resize();
return 0;
case WM_COMMAND:
switch (LOWORD(wparam))
{
case IDM_EXIT://菜单
DestroyWindow(hWinMain);
PostQuitMessage(0);
break;
default:
if (LOWORD(wparam)!= ID_EDIT)//非EDIT控件的其他菜单
{
wsprintf(szBuffer,szFormat,wparam);
MessageBox(hwnd,szBuffer,szCaption,MB_OK |MB_ICONINFORMATION);
}
break;
}
return 0;
case WM_SIZE:
_Resize();
return 0;
//通过指定TBSTYLE_TOOLTIPS样式和在.rc文件中创建一个STRINGTABLE
//来向工具栏添加工具提示
case WM_NOTIFY:
/*WM_NOTIFY消息的IParam参数指向一个NMHDR
为什么不像状态栏一样使用NMHDR结构?
因为不同控件的通知消息都使用 WM_NOTIFY消息,有些通知消息可能需要附带其他数据,这时仅使用一个NMHDR结 构来表达是不够的,Windows的处理办法是为需要附带其他数据的WM_NOTIFY消息定义不同的数据结构,但这些结构头部都是一个NMHDR结构,NMHDR结构以后才是其他字段,这样在得知通知码之前,把lParam参数指针当做一个NMHDR结构来处理总是正确的。
而且只有先把lParam参数指针当做NMHDR结构处理并从中获取通知码以后,才真正知道lParam指向的究竟是什么结构。
*/
/*方法一:使用NMTTDISPINFO结构*/
//检索显示工具提示窗口所需的信息
lpnmt = (LPNMTTDISPINFO)lparam;
//通知码,获取提示信息,与 TTN_GETDISPINFO相同
if (lpnmt->hdr.code == TTN_NEEDTEXT)
{
lpnmt->lpszText = (LPWSTR)(lpnmt->hdr).idFrom;
lpnmt->hinst = GetModuleHandle(0);
}
//可以在指定按钮的左侧插入一个按钮
else if (lpnmt->hdr.code == TBN_QUERYINSERT)
{
return TRUE;
}
//可以从工具栏中删除按钮
else if(lpnmt->hdr.code == TBN_QUERYDELETE)
{
return TRUE;
}
//按钮信息已复制到指定的缓冲区
else if (lpnmt->hdr.code == TBN_GETBUTTONINFO)
{
/*方法二:使用NMTOOLBAR结构---比较繁琐
RtlMoveMemory(&(NMToolbar.tbButton),sizeof(NMTOOLBAR)*NUM_BUTTONS+
tbb,sizeof(NUM_BUTTONS));
LoadString(GetModuleHandle(0),
(NMToolbar.tbButton).idCommand,szBuffer,sizeof(szBuffer));
NMToolbar.pszText = szBuffer;
NMToolbar.cchText = lstrlen(szBuffer);
*/
// 如果系统需要文本,则从资源中进行装载
LoadString(GetModuleHandle(0),
lpnmt->hdr.idFrom, // 字符串ID == 命令ID
szBuffer,
sizeof(szBuffer));
// 将结构指向字符串
lpnmt->lpszText = szBuffer;
return TRUE;
}
/*
//方法三:使用TOOLTIPTEXT结构,此结构已不再使用
lpToolTipText = (LPTOOLTIPTEXT)lparam;
if (lpToolTipText->hdr.code == TTN_NEEDTEXT)
{
// 如果系统需要文本,则从资源中进行装载
LoadString(GetModuleHandle(0),
lpToolTipText->hdr.idFrom, // 字符串ID == 命令ID
szBuffer,
sizeof(szBuffer));
// 将结构指向字符串
lpToolTipText->lpszText = szBuffer;
}
*/
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wparam, lparam);
}
//调整尺寸大小
void _Resize()
{
RECT rect, rect1;
SendMessage(hWinToolbar,TB_AUTOSIZE,0,0);
GetClientRect(hWinMain, &rect);
GetWindowRect(hWinToolbar, &rect1);
MoveWindow(hWinEdit, 0, rect1.bottom - rect1.top, rect.right, rect.bottom - (rect1.bottom - rect1.top), TRUE);
}
/****************************************************************
//新结构
//static NMTTDISPINFO nmt; //该结构取代了 TOOLTIPTEXT结构
/*
typedef struct tagNMTTDISPINFOA {
NMHDR hdr; //NMHDR结构包含有关通知的其他信息
LPSTR lpszText; //指向将以工具提示文本显示的以空字符结尾的字符串的指针
char szText[80]; //接收工具提示文本的缓冲区
HINSTANCE hinst; //处理包含包含用作工具提示文本的字符串资源的实例
UINT uFlags; //指示如何解释所包含的NMHDR结构的idFrom成员的标志
LPARAM lParam; //与工具关联的应用程序定义的数据
} NMTTDISPINFOA, *LPNMTTDISPINFOA;
*********************************************************************/
/*
typedef struct tagNMTOOLBARW {
NMHDR hdr; //NMHDR结构包含有关通知的其他信息
int iItem; //与通知代码关联的按钮的命令标识符
TBBUTTON tbButton; //BBUTTON结构,其中包含有关与通知代码关联的工具栏按钮的信息
int cchText; //按钮文字中的字符数
LPWSTR pszText; //包含按钮文本的字符缓冲区的地址
RECT rcButton; //RECT结构,它定义由按钮所覆盖的区域
} NMTOOLBARW, *LPNMTOOLBARW;
*/
/*===============================================================
NMHDR结构(richedit.h)包含有关通知消息的信息。
typedef struct _nmhdr {
HWND hwndFrom; //发送消息的控件的窗口句柄
UINT idFrom; //发送消息的控件的标识符
UINT code; //通知代码
} NMHDR;
================================================================*/
/*
//编写 工具提示通知处理程序时,需要使用 TOOLTIPTEXT 结构。 TOOLTIPTEXT 结构的成员包括:
typedef struct {
NMHDR hdr; // required for all WM_NOTIFY messages
LPTSTR lpszText; // 接收工具文本的字符串的地址
TCHAR szText[80]; // 接收工具提示文本的缓存区
HINSTANCE hinst; // 包含要用作工具提示文本的字符串的实例的句柄
UINT uflags; // flag indicating how to interpret the
// idFrom member of the NMHDR structure
// that is included in the structure
} TOOLTIPTEXT, FAR *LPTOOLTIPTEXT;
当处理 TTN_NEEDTEXT 通知消息时,请指定要用以下方式之一显示的字符串:
将文本复制到 szText 成员指定的缓冲区。
将包含文本的缓冲区的地址复制到 lpszText 成员。
将字符串资源的标识符复制到 lpszText 成员,并将包含该资源的实例的句柄复制到 hinst 成员。
===================================================================
//TBBUTTON结构(commctrl.h),包含有关工具栏中按钮的信息。
/*
typedef struct _TBBUTTON {
int iBitmap; //按钮在图片资源中的索引,以0开始
int idCommand; //按钮对应命令ID,一般和菜单命令对应
BYTE fsState; //按钮类型
BYTE fsStyle; //按钮样式
#if ...
BYTE bReserved[6];//保留
#else
BYTE bReserved[2];//保留
#endif
DWORD_PTR dwData; //自定义数据
INT_PTR iString; //按钮字符串的从零开始的索引,或者指向包含按钮文本的字符串缓冲区的指针
} TBBUTTON, *PTBBUTTON, *LPTBBUTTON;
*/
运行结果:
图11-3 工具栏控件
总结
实例Toolbar.c首先定义一个包含16个按钮的工具栏tbb[16]。在窗口过程的WM_CREATE消息处理中创建并初始化工具栏。此外还需要创建一个编辑控件。
创建工具栏控件的方法有两种:
第一种方法是调用CreateToolbarEx专用函数使用预定义位图创建一个平面按钮,带提示信息,可以按shilft键拖动按钮的工具栏控件。如果要支持工具栏的所有功能,则改用CreateWindowEx函数创建工具栏控件。
WM_COMMAND消息处理菜单消息。
WM_SIZE消息调整窗口大小。
WM_NOTIFY消息:处理工具栏控件消息。具体实现可以有三种方法。在前文已经详细讲述了三种不同用法。此实例使用了使用NMTTDISPINFO结构指针的方法。此处不再赘述。
【注意】实例使用了图标资源、菜单资源和String Table资源,在创建工具栏控件时,绑定了资源模块。