上一篇文章推荐了一个MP3剪切器,其中有关播放进度的调整存在问题,滑块本身拖动可以完成进度调整,但当通过鼠标单击目标位置时,滑块总是不能直接跳到想要达到的位置,使用体验较差。本文主要讨论一下如何调整滑块对鼠标消息的响应。
通过查询资料得知,MFC自带的滑块控件(SLIDER)并不支持单击调整位置,每次单击时滑块只能移动某一固定距离。此时如果想实现滑块移到鼠标位置,就需要对滑块的鼠标消息响应过程进行重写(Override,重写是指派生类的方法覆盖基类的方法,要求方法名、方法的参数都相同。这里区别于之前文章讲到的重载)。不过通过实际尝试和查阅资料发现,程序中重写了鼠标左键按下(WM_LBUTTONDOWN)的消息响应函数,但实际使用中点击滑块控件时并不能进入消息响应函数,导致调整进度条失败,但点击控件之外的地方可以正常进入。同样的重写思路在QT中是可以正常实现的,这里目前还没有搞清楚。
既然直接重写消息响应函数无效,只能考虑使用其他方法实现。这里用到一个虚函数:PreTranslateMessage,此函数是消息在响应之前调用。因此如果需要在程序响应某些消息之前做一些处理,可以通过实现此函数进行处理。
基本思路是当鼠标按下时,PreTranslateMessage先接收到鼠标消息,此时进行进度条的调整,代码如下(新建一个测试程序MFCTest):
BOOL CMFCTestDlg::PreTranslateMessage(MSG* pMsg)
{if (pMsg->message == WM_LBUTTONDOWN){CPoint point;GetCursorPos(&point);ScreenToClient(&point);CRect rect;GetDlgItem(IDC_SLIDER1)->GetWindowRect(&rect);ScreenToClient(&rect);if (point.y > rect.top && point.y < rect.bottom){if (point.x > rect.left && point.x < rect.right){return TRUE;}}}if (pMsg->message == WM_LBUTTONUP){CPoint point;GetCursorPos(&point);ScreenToClient(&point);CRect rect;GetDlgItem(IDC_SLIDER1)->GetWindowRect(&rect);ScreenToClient(&rect);if (point.y > rect.top && point.y < rect.bottom){if (point.x > rect.left && point.x < rect.right){int pos = 100 * (point.x - (rect.left + 10)) / ((rect.right - 10) - (rect.left + 10));m_MySlider.SetPos(pos);SetDlgItemInt(IDC_EDIT1, pos);}}}return CDialogEx::PreTranslateMessage(pMsg);
}
由以上代码看出,函数执行过程为:
1.此函数截获鼠标按下消息,判定如果鼠标位于滑块控件范围内则直接返回,此举目的是为了防止控件本身响应鼠标按下消息,导致滑块位置抖动。
2.函数截获鼠标松开消息,判定如果鼠标位于滑块控件范围内,则通过鼠标指针的坐标和滑块控件的相对位置进行计算得到滑块实际值(滑块总长度为100)。
3.调整滑块位置。
程序运行效果如下动画:
将此方法添加到上一篇文章的MP3剪切器中,测试运行效果:
新版MP3剪切器下载链接:
https://alwaywon.lanzoux.com/iAIiNoyxduj
或
https://pan.baidu.com/s/1-LRwlWDIkNBaulw9c55zgw 提取码:hfmn
总结:MFC是一套比较老的开发库了,对Windows API做了比较好的封装,但在如今对界面要求较高的情况下使用MFC开发桌面应用程序会显得比较繁琐甚至不能满足需求,因此如果比较追求界面体验,还是应该多多使用其他更新一点的集成库(框架),以提高开发效率。