【发布时间】:2014-07-16 13:17:17
【问题描述】:
我将编辑控件子类化为只接受浮点数。当用户输入无效时,我想弹出一个工具提示。我的目标行为就像一个带有ES_NUMBER 的编辑控件有:
到目前为止,我能够实现跟踪工具提示并在用户输入无效时显示它。
但是,工具提示放错了位置。我曾尝试使用ScreenToClient 和ClientToScreen 来解决此问题,但失败了。
以下是创建SCCE的说明:
1) 在 Visual Studio 中创建默认 Win32 项目。
2) 在您的stdafx.h 中添加以下包含,就在#include <windows.h> 下方:
#include <windowsx.h>
#include <commctrl.h>
#pragma comment( lib, "comctl32.lib")
#pragma comment(linker, \
"\"/manifestdependency:type='Win32' "\
"name='Microsoft.Windows.Common-Controls' "\
"version='6.0.0.0' "\
"processorArchitecture='*' "\
"publicKeyToken='6595b64144ccf1df' "\
"language='*'\"")
3) 添加这些全局变量:
HWND g_hwndTT;
TOOLINFO g_ti;
4) 这是一个简单的编辑控件子类过程(仅用于测试目的):
LRESULT CALLBACK EditSubProc ( HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch (message)
{
case WM_CHAR:
{
POINT pt;
if( ! isdigit( wParam ) ) // if not a number pop a tooltip!
{
if (GetCaretPos(&pt)) // here comes the problem
{
// coordinates are not good, so tooltip is misplaced
ClientToScreen( hwnd, &pt );
/************************** EDIT #1 ****************************/
/******* If I delete this line x-coordinate is OK *************/
/*** y-coordinate should be little lower, but it is still OK **/
/**************************************************************/
ScreenToClient( GetParent(hwnd), &pt );
/************************* Edit #2 ****************************/
// this adjusts the y-coordinate, see the second edit
RECT rcClientRect;
Edit_GetRect( hwnd, &rcClientRect );
pt.y = rcClientRect.bottom;
/**************************************************************/
SendMessage(g_hwndTT, TTM_TRACKACTIVATE,
TRUE, (LPARAM)&g_ti);
SendMessage(g_hwndTT, TTM_TRACKPOSITION,
0, MAKELPARAM(pt.x, pt.y));
}
return FALSE;
}
else
{
SendMessage(g_hwndTT, TTM_TRACKACTIVATE,
FALSE, (LPARAM)&g_ti);
return ::DefSubclassProc( hwnd, message, wParam, lParam );
}
}
break;
case WM_NCDESTROY:
::RemoveWindowSubclass( hwnd, EditSubProc, 0 );
return DefSubclassProc( hwnd, message, wParam, lParam);
break;
}
return DefSubclassProc( hwnd, message, wParam, lParam);
}
5) 添加以下WM_CREATE 处理程序:
case WM_CREATE:
{
HWND hEdit = CreateWindowEx( 0, L"EDIT", L"edit", WS_CHILD | WS_VISIBLE |
WS_BORDER | ES_CENTER, 150, 150, 100, 30, hWnd, (HMENU)1000, hInst, 0 );
// try with tooltip
g_hwndTT = CreateWindow(TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_ALWAYSTIP | TTS_BALLOON,
0, 0, 0, 0, hWnd, NULL, hInst, NULL);
if( !g_hwndTT )
MessageBeep(0); // just to signal error somehow
g_ti.cbSize = sizeof(TOOLINFO);
g_ti.uFlags = TTF_TRACK | TTF_ABSOLUTE;
g_ti.hwnd = hWnd;
g_ti.hinst = hInst;
g_ti.lpszText = TEXT("Hi there");
if( ! SendMessage(g_hwndTT, TTM_ADDTOOL, 0, (LPARAM)&g_ti) )
MessageBeep(0); // just to have some error signal
// subclass edit control
SetWindowSubclass( hEdit, EditSubProc, 0, 0 );
}
return 0L;
6) 初始化MyRegisterClass中的常用控件(return语句之前):
// initialize common controls
INITCOMMONCONTROLSEX iccex;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_BAR_CLASSES | ICC_WIN95_CLASSES |
ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES | ICC_STANDARD_CLASSES ;
if( !InitCommonControlsEx(&iccex) )
MessageBeep(0); // signal error
就是这样,SSCCE。
我的问题如下:
如何在主窗口中正确定位工具提示?我应该如何使用插入符号坐标进行操作?
有没有办法让工具提示句柄和工具信息结构不是全局的?
感谢您的宝贵时间。
最好的问候。
编辑#1:
通过删除子类过程中的ScreenToClient 调用,我已经设法实现了相当大的改进。 x坐标很好,y坐标可以稍微低一些。我仍然想以某种方式删除全局变量...
编辑#2:
我可以通过使用 EM_GETRECT 消息并将 y 坐标设置到格式化矩形的底部来调整 y 坐标:
RECT rcClientRect;
Edit_GetRect( hwnd, &rcClientRect );
pt.y = rcClient.bottom;
现在最终结果好多了。剩下的就是删除全局变量...
编辑#3:
看来我已经破解了!解决方案在EM_SHOWBALLOONTIP 和EM_HIDEBALLOONTIP 消息中!工具提示放置在插入符号位置,气球形状与图片上的相同,并且它会正确自动关闭。最棒的是我不需要全局变量!
这是我的子类过程 sn-p:
case WM_CHAR:
{
// whatever... This condition is for testing purpose only
if( ! IsCharAlpha( wParam ) && IsCharAlphaNumeric( wParam ) )
{
SendMessage(hwnd, EM_HIDEBALLOONTIP, 0, 0);
return ::DefSubclassProc( hwnd, message, wParam, lParam );
}
else
{
EDITBALLOONTIP ebt;
ebt.cbStruct = sizeof( EDITBALLOONTIP );
ebt.pszText = L" Tooltip text! ";
ebt.pszTitle = L" Tooltip title!!! ";
ebt.ttiIcon = TTI_ERROR_LARGE; // tooltip icon
SendMessage(hwnd, EM_SHOWBALLOONTIP, 0, (LPARAM)&ebt);
return FALSE;
}
}
break;
【问题讨论】:
-
MSDN Docs for TTM_TRACKPOSITION 表示 x/y 值是“在屏幕坐标中”
-
@EdwardClements:确实,在删除
ScreenToClient之后,我已经正确放置了它。我希望y坐标低一点,所以也许我应该在传递给ClientToScreen之前添加pt.y += 10...不过,我不知道如何摆脱全局变量...谢谢您的指点我错过了什么。最好的问候。 -
如果您使用带有
DT_CALCRECT标志的DrawText,您可以获得最后输入的字符的高度(.和I的值相同)。如果将此添加到 yPos,您将获得与您显示的图像相同的 yPos。我同意@EdwardClements,将信息填充到editWnd 的USERDATA 字段的好地方。 然而,我个人会使用SetProp函数来完成这项任务,它更加更干净、更清晰恕我直言。 (在销毁窗口之前不要忘记致电RemoveProp) -
@enhzflep:我已经设法获得了第二次编辑中描述的正确坐标。过去,您曾建议我在调用
SetWindowSubclasAPI 时将结构作为dwRefData发送。我有兴趣应用 Edward 的解决方案,但我需要帮助,因为这是我第一次这样做。如果您有兴趣提供帮助,我将不胜感激。谢谢你的时间:) -
我确实注意到了,只是想我会添加我认为可以重现图像的代码。使用 SetProp 的另一个优点是控件可以删除信息本身(而不是父 wnd 必须这样做)通过
dwRefData变量传递它要求您保留对父窗口 WndProc 中数据的引用。无论如何,有各种各样的选择。我会将完整的 (C::B) 代码发布为“答案”以确保完整性。
标签: c++ winapi tooltip editcontrol