C++ 如何检测悬停在静态Win32控件上的情况?
我在检测静态Win32控件上的悬停时遇到问题 这不是重复的问题,因为它查找多个静态控件,而不是在编译时查找静态控件的单个已知句柄 虽然这可以用另一种语言在几秒钟内完成,但在尝试了几个小时之后,我变得有点沮丧。我希望在这里得到答案 首先,我创建了一个名为Label的类。我在其中创建了一个静态控件窗口。现在我将静态作为标签C++ 如何检测悬停在静态Win32控件上的情况?,c++,winapi,C++,Winapi,我在检测静态Win32控件上的悬停时遇到问题 这不是重复的问题,因为它查找多个静态控件,而不是在编译时查找静态控件的单个已知句柄 虽然这可以用另一种语言在几秒钟内完成,但在尝试了几个小时之后,我变得有点沮丧。我希望在这里得到答案 首先,我创建了一个名为Label的类。我在其中创建了一个静态控件窗口。现在我将静态作为标签 // Create the label's handle. m_handle = CreateWindowEx(NULL, "static", m_text.c_str(),
// Create the label's handle.
m_handle = CreateWindowEx(NULL, "static", m_text.c_str(),
WS_CHILD | SS_LEFT | SS_NOTIFY, m_x, m_y, m_width, m_height,
m_parentWindow, (HMENU)(UINT_PTR)m_id, m_hInstance, NULL);
if (m_handle == NULL)
return false;
当鼠标悬停在此标签上时,应调用以下方法:
void Label::invokeOnMouseHover()
{
if (m_onMouseOver)
m_onMouseOver();
}
这将调用我的方法:
void lblName_onMouseOver()
{
MessageBox::show("Hovering!", "My Console",
MessageBoxButtons::Ok, MessageBoxIcon::Information);
}
以下是我从顶层创建它的方式:
Label lblName("This is a label.", 0, 0);
lblName.setVisible(true);
lblName.OnMouseOver(lblName_onMouseOver);
frm.add(lblName);
承认吧,这一层很漂亮
虽然我的回调对我的按钮和复选框控件很好,但我注意到静态有点不同
那么,让我们往下看几个层次:
这在主窗口的过程中:
case WM_MOUSEMOVE:
{
int xPos = LOWORD(lParam);
int yPos = HIWORD(lParam);
frm.setMousePos(xPos, yPos);
// Get the static's id
int id = // ?? Which static control id is it out of several?
// Obtain the control associated with the id.
X3D::Windows::Control *ctrl = frm.getControls().find(id)->second;
if (ctrl == NULL)
return 0;
// Check if this is a X3D Label control.
if (typeid(*ctrl) == typeid(X3D::Windows::Label))
{
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
if (xPos >= lbl->getX() &&
yPos >= lbl->getY() &&
(xPos < (lbl->getX() + lbl->getWidth())) &&
(yPos < (lbl->getY() + lbl->getHeight())))
{
if (lbl != NULL)
lbl->invokeOnMouseHover();
}
}
}
break;
如果这是油漆问题,这就是我在WndProc的情况
case WM_PAINT:
{
hDC = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
}
break;
更新#2:
替代方案解决了这个问题
如果将来有人在setWindowsSubClass()中遇到无法解析的外部符号的问题,请记住将以下内容添加到项目中,或者只对其进行杂注:
#pragma comment(lib, "comctl32.lib")
WM_MOUSEMOVE
为您提供的唯一识别信息是鼠标正在移动的HWND
(或已移动的HWND
)。报告的X/Y坐标相对于HWND
。这是您正在寻找的HWND
,因此您不需要搜索它,消息会将它提供给您
如果需要访问HWND
的控件ID,可以使用。但是请注意,每个HWND
都有自己的窗口过程,因此控件ID通常只对通知消息有用,例如WM_COMMAND
和WM_NOTIFY
,它们被发送到控件的父窗口(即使这样,此类通知也会携带子控件的HWND
)
当鼠标移动到特定的HWND
上时,WM_MOUSEMOVE
仅发布到该HWND
的消息过程(或发布到捕获鼠标的HWND
)。听起来您希望它被发布到控件的父窗口,但事实并非如此。这就是为什么没有调用WM\u MOUSEMOVE
处理程序的原因。您在错误的级别处理消息。您需要准备在每个控件的基础上处理消息,而不是使用控件自己的消息过程
通过SetWindowLongPtr(GWLP\u USERDATA)
、SetWindowSubClass()
或SetProp()
,将控件的此指针存储在其关联的HWND
本身中会更有效,然后,您的消息处理程序可以在需要时访问消息报告的HWND
的控件*
指针,例如:
// Create the label's handle.
m_handle = CreateWindowEx(NULL, TEXT("static"), m_text.c_str(),
WS_CHILD | SS_LEFT | SS_NOTIFY, m_x, m_y, m_width, m_height,
m_parentWindow, (HMENU)(UINT_PTR)m_id, m_hInstance, NULL);
if (!m_handle)
return false;
SetWindowLongPtr(m_handle, GWLP_USERDATA, (LONG_PTR)(X3D::Windows::Control*)this);
m_oldproc = (WNDPROC) SetWindowLongPtr(m_handle, GWL_WNDPROC, (LONG_PTR)&MyWndProc);
...
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MOUSEMOVE:
{
...
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (ctrl)
{
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl)
lbl->invokeOnMouseHover();
}
break;
}
...
}
return CallWindowProc(m_oldproc, hWnd, uMsg, wParam, lParam);
}
//创建标签的句柄。
m_handle=CreateWindowEx(空,文本(“静态”),m_TEXT.c_str(),
WS|u CHILD | SS|u LEFT | SS|u NOTIFY,m|x,m|y,m|u width,m|u height,
m_parentWindow,(humenu)(UINT_PTR)m_id,m_hInstance,NULL);
if(!m_handle)
返回false;
SetWindowLongPtr(m_句柄,GWLP_用户数据,(LONG_PTR)(X3D::Windows::Control*)this);
m_oldproc=(WNDPROC)SetWindowLongPtr(m_handle,GWL_WNDPROC,(LONG_PTR)和MyWndProc);
...
LRESULT回调MyWndProc(HWND HWND、UINT uMsg、WPARAM WPARAM、LPARAM LPARAM)
{
开关(uMsg)
{
案例WM_MOUSEMOVE:
{
...
X3D::Windows::Control*ctrl=(X3D::Windows::Control*)GetWindowLongPtr(hWnd,GWLP_USERDATA);
如果(ctrl)
{
//检查这是否是X3D标签控件。
标签*lbl=动态投影(ctrl);
如果(lbl)
lbl->invokeOnMouseHover();
}
打破
}
...
}
返回CallWindowProc(m_oldproc、hWnd、uMsg、wParam、lParam);
}
或者:
// Create the label's handle.
m_handle = CreateWindowEx(NULL, TEXT("static"), m_text.c_str(),
WS_CHILD | SS_LEFT | SS_NOTIFY, m_x, m_y, m_width, m_height,
m_parentWindow, (HMENU)(UINT_PTR)m_id, m_hInstance, NULL);
if (!m_handle)
return false;
SetWindowSubclass(m_handle, &MySubclassProc, 1, (DWORD_PTR)(X3D::Windows::Control*)this);
...
LRESULT CALLBACK MySubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &MySubclassProc, uIdSubclass);
break;
case WM_MOUSEMOVE:
{
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) dwRefData;
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl)
lbl->invokeOnMouseHover();
break;
}
...
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
//创建标签的句柄。
m_handle=CreateWindowEx(空,文本(“静态”),m_TEXT.c_str(),
WS|u CHILD | SS|u LEFT | SS|u NOTIFY,m|x,m|y,m|u width,m|u height,
m_parentWindow,(humenu)(UINT_PTR)m_id,m_hInstance,NULL);
if(!m_handle)
返回false;
setWindowsSubclass(m_handle和MySubclassProc,1,(DWORD_PTR)(X3D::Windows::Control*)this);
...
LRESULT回调MySubclassProc(HWND HWND、UINT uMsg、WPARAM WPARAM、LPARAM LPARAM、UINT_PTR uIdSubclass、DWORD_PTR dwRefData)
{
开关(uMsg)
{
案例WM\NCU:
移除Windows子类(hWnd和MySubclassProc、uIdSubclass);
打破
案例WM_MOUSEMOVE:
{
X3D::Windows::Control*ctrl=(X3D::Windows::Control*)DWREF数据;
//检查这是否是X3D标签控件。
标签*lbl=动态投影(ctrl);
如果(lbl)
lbl->invokeOnMouseHover();
打破
}
...
}
返回def子类proc(hWnd、uMsg、wParam、lParam);
}
这篇文章没有回答您提出的问题,但您应该阅读它。这是很重要的,在我上面相当尖刻的评论之后,它是以一种乐于助人的精神发布的。我希望你觉得是这样
这个类库要出问题了。这样的代码(使用动态\u cast
):
然后:
class Label : public Control // Derived class
{
...
virtual void onMouseHover (...) override { ... }
...
};
现在谈谈我的第二点:您将发现,特别是对于对话框,您的应用程序将需要处理大量基类不理解(或不关心)的消息
你打算怎么做?对于应用程序(或类库中实现的特定类型的控件)感兴趣的每一条新消息,是否要向基类添加代码?这不是一个很有吸引力的前景
没有
// Create the label's handle.
m_handle = CreateWindowEx(NULL, TEXT("static"), m_text.c_str(),
WS_CHILD | SS_LEFT | SS_NOTIFY, m_x, m_y, m_width, m_height,
m_parentWindow, (HMENU)(UINT_PTR)m_id, m_hInstance, NULL);
if (!m_handle)
return false;
SetWindowSubclass(m_handle, &MySubclassProc, 1, (DWORD_PTR)(X3D::Windows::Control*)this);
...
LRESULT CALLBACK MySubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &MySubclassProc, uIdSubclass);
break;
case WM_MOUSEMOVE:
{
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) dwRefData;
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl)
lbl->invokeOnMouseHover();
break;
}
...
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
case WM_MOUSEMOVE:
{
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) dwRefData;
// Check if this is a X3D Label control.
Label *lbl = dynamic_cast<X3D::Windows::Label*>(ctrl);
if (lbl)
lbl->invokeOnMouseHover();
break;
}
class Control // Base class
{
...
virtual void onMouseHover (...) { }
...
};
class Label : public Control // Derived class
{
...
virtual void onMouseHover (...) override { ... }
...
};
typedef INT_PTR (Window::*MESSAGE_HANDLER)
(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// Register a message handler
void Window::RegisterMessageHandler (UINT uMsg, MESSAGE_HANDLER handler);
class Control // Base class
{
...
std::unordered_map <UINT, MESSAGE_HANDLER> m_message_map;
...
};
void Window::RegisterMessageHandler (UINT uMsg, MESSAGE_HANDLER handler)
{
m_message_map.emplace (uMsg, handler);
}
LRESULT CALLBACK MySubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
X3D::Windows::Control *ctrl = (X3D::Windows::Control*) dwRefData;
auto handler ctrl->m_message_map.find (uMsg);
if (handler != ctrl->m_message_map.end ())
return handler.second (hWnd, uMsg, wParam, lParam);
...
}