推测的处理过程:
要在标题栏上增加一个问号按钮,得参考一些系统菜单操作API;
点这个问号按钮会产生一个系统消息,我在处理这个消息的时候把对话框的鼠标光标改为带问号的指针,用这个带问号的指针点击对话框的某个元素,就产生一个“WM_LBUTTONDOWN”的消息,处理这个消息,根据鼠标光标的位置判定鼠标点击的是对话框上的那个元素,获取这个元素的帮助字符串,创建一个没有Title的很小的窗口,把这个小窗口在鼠标光标位置处Pop出来,然后在上面Draw一些帮助字符串,当这个小窗口失去焦点,或被用户用鼠标点击了一下,或按了一下键盘什么的,就会Hide起来,当用户又执行了上述的“问号点击操作”之后,这个小窗口才会再次Show出,最后在对话框关闭的时候Destroy这个小窗口。
首先在对话框的标题栏上增加一个问号按钮就不容易,有没有简单些的方法?当然有,看下图:
其实只需要在对话框属性上选中Context Help这个选项就OK了;
WM_HELP,MSDN上能找到,我大致说明一下:
lphi = (LPHELPINFO) lParam;
typedef struct tagHELPINFO
{
UINT cbSize;
int iContextType
int iCtrlId;
HANDLE hItemHandle;
DWORD dwContextId;
POINT MousePos;
} HELPINFO, FAR *LPHELPINFO;
WM_HELP的lParam参数是指向HELPINFO这个结构的指针,我们需要用到这个结构的几个成员,一是iContextType==HELPINFO_WINDOW的时候,iCtrlId的值,这个值就是点击的对话框上元素的ID,具体看resource.h中的定义;另一个就是MousePos,光标位置;至于其它,就自己看看MSDN,在别处会用到的,但这里就简单些,暂时不用,此文只是起个抛砖引玉的作用。
现在只剩下一个问题了,那就是如何实现那个pop出来的小窗口?我马上想到了tooltip control,什么是tooltip control?我想你天天见,天天用,只是不知道它就是tooltip control而已,看看下面这截图:
当鼠标光标停留在工具栏上片刻(通常是你最长的双击间隔时间),光标下面就会出现一个小提示窗口,你把鼠标移动到别处后,这个小提示就消失或者换成别的了;
最后我是自己手动实现了这么一个窗口的,下面我将给出代码:
在对话框的WM_INITDIALOG消息处理中:
{
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = MyToolTipProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = g_hInstance;
wndclass.hIcon = LoadIcon(g_hInstance, (LPCTSTR)IDI_ICONAPP);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = TEXT("MyToolTipWindow");
RegisterClass (&wndclass);
hwndToolTip = CreateWindow(TEXT("MyToolTipWindow"), TEXT(""),
WS_POPUP|WS_BORDER, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, hDlg, (HMENU)NULL, g_hInstance, NULL);
}
其中"MyToolTipWindow"是我要注册的窗口类名,你可以起别的名字,注意这个窗口是没有标题栏的,hDlg是这个小小窗口的父窗口,也就是对话框的句柄,g_hInstance是进程实例句柄,我是把它存在一个全局变量中的,所以有个“g_”前缀,还要注意一下,把hwndToolTip作为全局变量或者static变量,以保存它的值,因为之后我们还需要用。
在对话框的WM_DESTROY消息中:
{
DestroyWindow(hwndToolTip);
}
把这个tooltip control删除。
好,现在我们看看"MyToolTipWindow"这个窗口的消息处理函数MyToolTipProc:
#define WM_USER_SETTEXT (WM_USER+10) //wParam : Pointer to the TCHAR string; lParam : NULL
#define WM_USER_SHOWWINDOW (WM_USER+11) //wParam: mouse.x; lParam : mouse.y
// Message handler for MyToolTipWindow.
LRESULT CALLBACK MyToolTipProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static TCHAR szToDisp[1024];
switch (message)
{
case WM_CREATE:
return TRUE;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_KEYDOWN:
case WM_KILLFOCUS:
ShowWindow(hwnd, FALSE);
break;
case WM_PAINT:
{
RECT rect;
PAINTSTRUCT ps;
HDC hdc = BeginPaint (hwnd, &ps);
GetWindowRect(hwnd, &rect);
rect.right = rect.right-rect.left;
rect.bottom = rect.bottom-rect.top;
rect.top = 0;
rect.left = 0;
DrawText(hdc, szToDisp, lstrlen(szToDisp), &rect, DT_LEFT|DT_WORDBREAK);
EndPaint (hwnd, &ps);
}
break;
case WM_USER_SETTEXT:
lstrcpy(szToDisp, (TCHAR *)wParam);
return TRUE;
case WM_USER_SHOWWINDOW:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint (hwnd, &ps);
DWORD dwRtn = GetTabbedTextExtent(hdc, szToDisp, lstrlen(szToDisp), 0, NULL);
EndPaint (hwnd, &ps);
UINT iScreenWidth = GetSystemMetrics(SM_CXFULLSCREEN);
UINT iXPos = wParam+LOWORD(dwRtn)+4>iScreenWidth?iScreenWidth-LOWORD(dwRtn)-4:wParam;
MoveWindow(hwnd, iXPos, lParam+21, LOWORD(dwRtn)+4, HIWORD(dwRtn)+3, FALSE);
ShowWindow(hwnd, TRUE);
}
return TRUE;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
接到WM_LBUTTONDOWN,WM_RBUTTONDOWN,WM_KEYDOWN,WM_KILLFOCUS这几个消息之后,就把窗口隐藏起来,用WM_USER_SETTEXT这个消息设置要在mytooltip中显示的文本,然后用WM_USER_SHOWWINDOW把mytooltip show出来,WM_USER_SHOWWINDOW里面那些处理是为了根据要显示的文本和鼠标的位置调整mytooltip的窗口大小和位置,其它倒是很简单,没什么好解释的。
最后就是在对话框处理函数中加入对WM_HELP消息的响应:
{
LPHELPINFO lphi = (LPHELPINFO) lParam;
if (lphi->iContextType==HELPINFO_WINDOW)
{
TCHAR szDisp[256];
memset(szDisp, 0, sizeof(szDisp));
LoadString(g_hInstance, lphi->iCtrlId, szDisp, 255);
SendMessage(hwndToolTip, WM_USER_SETTEXT, (WPARAM)(szDisp), NULL);
if (0==lstrlen(szDisp))
break;
SendMessage(hwndToolTip, WM_USER_SHOWWINDOW, lphi->MousePos.x, lphi->MousePos.y);
}
}
LoadString函数能在程序资源中Load出相应对话框元素ID的string,这个需要自己编辑一下string资源。大功告成;
如果文本太长了,不会自动换行,很难看,而且没有“阴影效果”,这两个功能就交给读者你了;(#add 可设置CToolTipCtrl的宽度,)