MFC 定时器轮询实现按住按钮进度条增加(鼠标悬停/长按检测)

本文详细讲解如何在 MFC 对话框中实现按钮长按检测。主要内容包括:① 为什么 OnMouseMove 在鼠标进入按钮后会失效;② 定时器轮询的设计思路;③ GetCursorPosGetAsyncKeyStatePtInRect 等关键 API 的使用;④ 进度条控件的实时更新;⑤ 部分代码及注释。

思路一:(未能成功)

利用MFC的ON_WM_MOUSEMOVE()消息及其响应函数OnMouseMove()结合使用,当鼠标移动时,会不停触发ON_WM_MOUSEMOVE()消息,然后不断调用其响应函数,响应函数内填写相关逻辑代码,大致内容为:获取当前鼠标坐标,与按钮所在区域不断对比,若鼠标在按钮区域内,则返回TRUE,让按钮内文本变为:"鼠标来了",(验证了这个后续再写按住和进度条增加)

响应函数代码如下:

cpp 复制代码
void CMouse_Press_DetectDlg::OnMouseMove(UINT nFlags, CPoint point)
{
    CPoint screenPt = point;
    ClientToScreen(&screenPt);
    
    CRect rcBtn;
    GetDlgItem(IDC_BUTTON1)->GetWindowRect(&rcBtn);
    
    CPoint clientPt = point;
    // 【诊断】输出坐标数值
    CString dbg;
    dbg.Format(_T("client=(%d,%d) screen=(%d,%d) btn=(%d,%d,%d,%d) in=%d"),
        clientPt.x, clientPt.y,
        screenPt.x, screenPt.y,
        rcBtn.left, rcBtn.top, rcBtn.right, rcBtn.bottom,
        rcBtn.PtInRect(screenPt));
    
    SetWindowText(dbg);  // 临时显示在标题栏
	
   
    if (rcBtn.PtInRect(screenPt))
    {
        SetDlgItemText(IDC_BUTTON1, _T("鼠标来了"));
    }
    else
    {
        SetDlgItemText(IDC_BUTTON1, _T("按钮"));
    }
    CDialog::OnMouseMove(nFlags, point);
}

此处需要注意的是:OnMouseMovepoint 参数是客户区坐标(大窗口) ,而 GetWindowRect 返回的是屏幕坐标 ,两者坐标系不同,必须统一。故需要 CPoint screenPt = point;

ClientToScreen(&screenPt);这样将窗口坐标转化成屏幕坐标。

PtInRect:判断点是否在矩形区域内(包括左、上边界,不包括右、下边界)

理想很美好,现实很残酷。这段代码逻辑上是没什么问题的,但是执行时会出现如下问题:

原始按钮名设定位Button1,在窗口外鼠标活动时,文本仍为Button1,工作正常;

当鼠标移动到窗口内后,文本变成"按钮",仍然正常。

但当鼠标移动到按钮上方时,按钮名称不变,仍为"按钮"二字。

根据显示在标题栏中的坐标信息显示,当鼠标移动到按钮上方时,除非鼠标离开按钮区域,否则坐标不会再变化,即并未不断触发响应函数。

这是因为:鼠标消息(如 WM_MOUSEMOVE)的目标窗口光标下方的窗口决定,当鼠标在对话框客户区运动时,消息正常发送,响应函数正常执行。

而当鼠标移动到按钮上方时,按钮是一个独立的子窗口,目标窗口转换为按钮,鼠标消息发送给了按钮,按钮默认不处理这个消息,故不会执行大窗口消息的响应函数OnMouseMove(),自然也不会触发函数里更新标题栏数据,更新按钮文本的代码。

直接解决方案:(未实现)

直接重写Button类,或者强制设置鼠标消息接收目标,后续可能会尝试,会填坑

思路二:(已经实现)

绕过MFC的消息机制,直接采用定时器进行轮询,不依赖鼠标消息的触发,自然也无需担心响应函数是否响应的问题。

具体就是设置一个定时器,固定时间间隔触发,触发执行逻辑其实和上面的是一样的,具体执行为

cpp 复制代码
void CMouse_Press_DetectDlg::OnTimer(UINT_PTR nID)
{

	CPoint screenPt;
	GetCursorPos(&screenPt);
	
	CRect rcBtn;
	GetDlgItem(IDC_BUTTON1)->GetWindowRect(&rcBtn);
	
	if (rcBtn.PtInRect(screenPt))
		SetDlgItemText(IDC_BUTTON1, _T("鼠标来了"));
	else
            SetDlgItemText(IDC_BUTTON1, _T("按钮"));
}

需要注意的是,定时器需要初始化,故初始化代码为:SetTimer(1, 50, NULL);放置于InitDialog中,消息为:ON_WM_TIMER(),头文件定义OnTimer时需要在返回类型前面加上afx_msg,不过也可留可不留

afx_msg 是 MFC 的文档标记宏,展开后是空的,只用来告诉阅读代码的人"这是一个消息处理函数",去掉也不影响程序运行。但为了遵循 MFC 规范和类向导工具识别,建议保留

然后去实现长按按钮让进度条增长,具体思路如下:

由于原生Button类不存在按住这个状态,故要不还是重写这个类,要不就是去找寻其他方案,这里我选择的是找其他方案:检测鼠标左键状态,当鼠标在按钮上时,然后鼠标左键按下时,触发进度条增长效果,定时器函数实现如下:

cpp 复制代码
void CMouse_Press_DetectDlg::OnTimer(UINT_PTR nID)
{
	CPoint screenPt;
	GetCursorPos(&screenPt);
	
	CRect rcBtn;
	GetDlgItem(IDC_BUTTON1)->GetWindowRect(&rcBtn);

    CPoint clientPt = screenPt;
    // 【诊断】输出坐标数值
	CString dbg;
	dbg.Format(_T("client=(%d,%d) screen=(%d,%d) btn=(%d,%d,%d,%d) in=%d"),
		clientPt.x, clientPt.y,
		screenPt.x, screenPt.y,
		rcBtn.left, rcBtn.top, rcBtn.right, rcBtn.bottom,
		rcBtn.PtInRect(screenPt));
    
	SetWindowText(dbg);
	
	if (rcBtn.PtInRect(screenPt))
		SetDlgItemText(IDC_BUTTON1, _T("鼠标来了"));
	else
            SetDlgItemText(IDC_BUTTON1, _T("按钮"));

	BOOL bLeftPressed = (GetAsyncKeyState(VK_LBUTTON) & 0x8000) != 0;
	
	int iPos = m_Progress_Slow.GetPos();
	if (rcBtn.PtInRect(screenPt) && bLeftPressed)
	{
		if (iPos < 100)
		{
			iPos++;
			m_Progress_Slow.SetPos(iPos);
		}
		SetDlgItemText(IDC_BUTTON1, _T("按住中"));
	}
}

此时就可以完成,按住按钮,进度条不断增加的效果了。但是逻辑上仍然存在漏洞:

判断语句里写的是:鼠标左键按下,且鼠标在按钮范围内,就可以实现增长,那么满足这个条件的共有两个情况,第一种:想要的情况,在按钮内按住按钮,进度条不断增长;第二种:非常规情况,我在按钮外按下左键,然后在一直按着的情况下将光标移动到按钮上,同样可以实现增长,很明显第二种不是我们想要的,还需要再打一些补丁。

未完待续......

相关推荐
AIminminHu5 小时前
OpenGL渲染与几何内核那点事-项目实践理论补充(三-1-(3):番外篇-当你的CAD打开“怪兽级”STL时:从内存爆炸到零拷贝的极致优化)
开发语言·c++·线程·多线程
j_xxx404_5 小时前
力扣题型--链表(两数相加|两两交换链表中的节点|重排链表)
数据结构·c++·算法·leetcode·蓝桥杯·排序算法
kyle~5 小时前
FANUC 机械臂 --- 配置字
网络·c++·机器人·ros2
oldmao_20005 小时前
第八章 设计并发代码
开发语言·c++·多线程编程·并发编程
芒果披萨5 小时前
日志管理 logging
java·开发语言·c++
天若有情6736 小时前
颠覆C++传统玩法!Property属性与伪类,开辟静态语言新维度
java·c++·servlet
CylMK6 小时前
题解:P11625 [迷宫寻路 Round 3] 迷宫寻路大赛
c++·数学·算法
计算机安禾6 小时前
【数据结构与算法】第44篇:堆(Heap)的实现
c语言·开发语言·数据结构·c++·算法·排序算法·图论
tankeven6 小时前
HJ175 小红的整数配对
c++·算法