BEGIN_MESSAGE_MAP(CPQVDirect2DControlsDlg, CDialogEx)
ON_WM_TIMER()
ON_WM_PAINT()
ON_WM_DRAWITEM()
ON_WM_ERASEBKGND()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &CPQVDirect2DControlsDlg::OnBnClickedTestButton)
ON_BN_CLICKED(IDC_CHECK1, &CPQVDirect2DControlsDlg::OnBnClickedTestCheckbox)
ON_BN_CLICKED(IDOK, &CPQVDirect2DControlsDlg::OnBnClickedOk)
ON_NOTIFY(NM_CUSTOMDRAW, IDC_SLIDER1, &CPQVDirect2DControlsDlg::OnNMCustomdrawSlider1)
ON_BN_CLICKED(IDC_BUTTON3, &CPQVDirect2DControlsDlg::OnBnClickedButton3)
ON_BN_CLICKED(IDC_BUTTON2, &CPQVDirect2DControlsDlg::OnBnClickedButton2)
ON_WM_MOUSEMOVE() // I HAVE ADDED THIS
END_MESSAGE_MAP()
void CPQVDirect2DControlsDlg::OnMouseMove(UINT nFlags, CPoint point)
{
CRect rect;
CButton* pButton = static_cast<CButton*>(GetDlgItem(IDC_BUTTON1)); // Replace IDC_MY_BUTTON with your button's ID
// Check if the mouse is over the button
if (pButton != nullptr && pButton->GetSafeHwnd() != nullptr) {
pButton->GetClientRect(&rect);
pButton->ScreenToClient(&point);
if (rect.PtInRect(point)) {
// Change the button's color when the mouse hovers over it
int i = 10;
pButton->Invalidate();
}
else {
// Revert to the default color when the mouse leaves
int i = 20;
pButton->Invalidate();
}
}
CDialogEx::OnMouseMove(nFlags, point);
}
字符串
我的断点应该在int i = 10;
,但它总是去int i = 20;
,甚至我的鼠标在IDC_BUTTON1。有人可以帮助如何改变鼠标悬停的按钮边框吗?我是MFC C++的新手
4条答案
按热度按时间rsl1atfo1#
正如在其他答案中提到的,在试图确定鼠标指针是否在按钮控件内的代码中存在一个错误。它检索按钮的客户端大小(在客户端坐标中),随后将这些坐标解释为屏幕坐标。计算的结果是确定性的,但没有意义。
然而,这是一个Red Herring,它分散了核心问题的注意力:鼠标输入总是1传递到鼠标光标下的窗口。当鼠标光标在按钮上移动时,
WM_MOUSEMOVE
消息会发布到按钮窗口。只要鼠标光标在子窗口内,其父窗口(对话框)就不会接收WM_MOUSEMOVE
消息。因此,有必要处理按钮控件中的
WM_MOUSEMOVE
消息。系统允许客户端代码通过一种称为subclassing的技术来修改控件的标准行为。子类化是一个复杂的主题,但MFC提供了强大的工具,使它的使用更加方便2。
工具
使用MFC子类别化控件需要下列步骤:
1.定义衍生自个别MFC控件 Package 函数3的类别。
1.通过在其父对话框中声明类成员来示例化此类的对象。
1.连接邮件路由。
1.自定义按钮行为。
派生自定义类
派生一个自定义类就像键入
class CMyButton : public CButton {};
一样简单。然而,消息路由需要更多的基础设施(即消息Map),我们将让 Add New Item 向导来为我们完成,而不是自己键入:mfc class
后,筛选清单会包含一个名为 MFC Class 的项目。选取它,然后按一下 Add。CMyButton
)以及 * 基底类别 *(CButton
)。按一下 * 确定 *,最后会建立下列档案:字符串
型
我已经删除了
DECLARE_DYNAMIC
和IMPLEMENT_DYNAMIC
宏,因为我们不需要MFC的运行时类型信息支持。示例化
CMyButton
类定义了
CMyButton
类之后,我们必须在需要的时候示例化它。最方便的方法是将它声明为使用它的对话框类的(private
)类成员:型
这样做会将
m_MyButton
的生存期与其父对话框的生存期联系起来。它会在对话框旁边构造,并在对话框被破坏时自动销毁。连接消息路由
到目前为止,我们已经创建了一个“自定义”按钮控件,它的生命周期与其父对话框的生命周期一致。但是,按钮消息仍然要通过标准按钮控件实现。要通过
CMyButton
的消息Map来路由它们,本机按钮控件需要与CMyButton
类相关联。这是通过在对话框的
DoDataExchange
覆盖中调用DDX_Control
来完成的(有关更多详细信息,请参见Dialog Data Exchange):型
这样做会将
CMyButton
的消息Map链接到默认按钮消息处理之前,从而允许C++实现增加或覆盖默认处理。自定义行为
此时,一切都已经设置好了,
CMyButton
可以控制与之关联的本机控件的外观和感觉。实际上,由message map macros发出的代码已经在执行,即使是无关紧要的。让我们通过连接WM_MOUSEMOVE
消息处理程序来快速改变这一点。型
型
在调试器下运行这个程序,每当鼠标在
CMyButton
控件上移动时,调试器output window中就会显示Hit
。这是一个进步,但是问题还没有解决。回想一下,鼠标消息只会传递到鼠标光标下的窗口。由于上面的实现只与按钮控件有关,因此我们不必再仔细检查鼠标是否在控件内部。尽管如此,它还是引入了一个新的问题:我们如何知道鼠标何时移出了控件?
要解决此问题,我们需要联系系统:它可以在任何时候发布
WM_MOUSELEAVE
消息。但是事情并不像将ON_WM_MOUSELEAVE()
放到我们的消息Map中并完成它那么简单。我们必须通过调用TrackMouseEvent
来主动请求鼠标跟踪。复杂性并没有到此为止。WM_MOUSELEAVE
是一次性通知,一旦消息生成,跟踪请求就会被取消。每当鼠标进入我们的控件时,我们都必须重新请求跟踪。而there is noWM_MOUSEENTER
message也必须在内部记录状态信息。这是一个相当多的信息需要消化。幸运的是,实现并不像前面的解释那么令人生畏。下面是下一个迭代:
型
型
这还不算太糟。代码仍然相当紧凑,浏览一下就可以推断出它的意图。这出奇地容易实现。
可疑的容易。
实际上,这是一个回归,默认的悬停高亮显示从一个按钮消失了,该按钮的行为通过
CMyButton
的实现重定向(enabling visual styles使这很容易观察)。这个缺陷不是刚刚才介绍的。它是在我们开始定制行为时开始出现的,我们在第一次迭代中选择处理
WM_MOUSEMOVE
消息。OnMouseMove
处理程序做的不多。关键的细节不在于它做了什么。而在于它不做什么:也就是说,获取默认实现。MFC子类链表示为类层次结构。为了让默认实现回到循环中,我们需要调用基类:型
型
我继续介绍了
BaseClass
类型别名,如果我们决定从一个不同的类派生CMyButton
,那么所需要的只是修改一行代码。1* 除非该窗口被禁用(参见
EnableWindow
),或者鼠标输入被其他窗口捕获(参见SetCapture
)。*[2]这一主题仍然很复杂,工具无法使使用者摆脱理解其内在和复杂性的必要性。
3* 以下内容源自
CButton
,仅用于说明。CMFCButton
更适合,因为它已经提供了我们手动实现的自定义点。*4* 事后看来,我应该添加
.h
/.cpp
文件并手动记下。*qni6mghb2#
你可能想要这个:
字符串
但还有一个问题:
OnMouseMove
只会在鼠标指针不在可点击控件上时被调用,因为鼠标移动事件被传递到鼠标光标下的窗口(注意按钮 * 是 * 一个窗口)。你可以通过查看调试输出来检查这一点,其中将显示TRACE
s。如果取消注解
pButton->EnableWindow(false);
,按钮将被禁用,即使您将鼠标悬停在按钮上,鼠标移动事件也将被传递到对话框窗口(而不是按钮)。这不是真正有帮助的,它只是为了演示。你要找的真实的答案是here。
ih99xse13#
正如许多人所指出的,
OnMouseMove()
传递了一个在对话框客户端坐标中的点。不要为按钮调用GetClientRect()
,这没有意义,因为左上角的点总是(0,0)-这个函数基本上返回控件的客户端区域大小。相反,为按钮调用GetWindowRect()
,它返回窗口的rect屏幕坐标。然后当然你必须将它们转换为对话框客户端坐标,或者将point
的客户端坐标转换为屏幕,然后检查点是否位于按钮的矩形中。pkwftd7m4#
已更新答案。
这应该可以:
字符串