MFC的OnMouseMove移动位置和OnMouseWheel缩放实现

来源:互联网 发布:mac分销渠道设计方案 编辑:程序博客网 时间:2024/06/05 14:22

1. 基本作用

OnMouseMove响应鼠标移动时间

OnMouseWheel响应鼠标中键的滚动

 

2. 参数说明

afx_msgvoidOnMouseMove(UINTnFlags, CPointpoint);

nFlags说明:指示虚拟按键是否按下 ,此参数可以是任何下列值的组合

MK_CONTROL 当CTRL键按下时
MK_LBUTTON 当鼠标左键按下时
MK_MBUTTON 当鼠标中键按下时(滚动不属于按下)
MK_RBUTTON 当鼠标右键按下时.
MK_SHIFT 当SHIFT按下时。

point说明:鼠标的X,Y坐标:该坐标为鼠标相对所在窗口左上角为基点的位置,是一个相对位置而不是在屏幕像素上的绝对位置。

 

afx_msg BOOL OnMouseWheel( UINT nFlags, shortzDelta, CPointpt );

nFlags同上

zDelta:大于0时为向上滚动,小于0时为向下滚动。A value less than zero indicates rotating back (toward the user) while a value greater than zero indicates rotating forward (away from the user). Windows下通常向上滚动缩小/窗口上移,反之放大/下移

pt::鼠标的X,Y坐标,是以其父窗口的左上角为基点的。Specifies the x- and y-coordinate of the cursor. These coordinates are always relative to the upper-left corner of the window.

 

3. 移动的效果实现

要实现移动,例如鼠标左键拖动butoon/图片在窗口上移动,实现的结果附加要求:鼠标放在button/图片的A点,移动之后,鼠标点依然在A点上

我们通过

a. 检测鼠标已在button/图片上(确保不是在哪都可以移动图标),并且左键按下

b. 记录鼠标当前点和上个点,计算两个的偏移值,然后使用这个偏移值来移动button/图片(MoveWindows)

(记录上个点的方法可以使用静态变量,移动完毕后,把当前点赋值给静态变量)(具体实现可以灵活处理)

实现原理是:相对静止---鼠标和对象相对位置不变,鼠标的偏移量,就是我们对象的偏移量

 

4. 缩放的效果实现(以鼠标点为中心缩放)

要实现缩放,例如中件滑轮向上滑动缩小,向下滑动放大button/图片,实现附加要求:鼠标放在button/图片的A点,缩放之后,鼠标点依然在A点上,缩放是以鼠标点为中心

a. 同样检测鼠标已在button/图片上(确保不是在哪都可以缩放button/图片)

b. 获取当前button/图片的高和宽(使用getClientRect)

c. 获取当前pt点x,y相对于button/图片位置,然后计算该位置相对于宽和高的比值

d. 判断zDelta正负确定放大缩小(按比例调整图片高度和宽度),并调整图片左上点(left,top)的位置,确保c中的比值不变(---确保了以鼠标所在点为中心放大或缩小)

实现原理是:相对移动---鼠标和所在对象点位置不变,鼠标所在对象点的周围 长和宽 成比例的缩放


鼠标事件

在日常用到的很多软件里,当鼠标停留在某个菜单或者按钮上一定事件,会弹出关于这个空间的一些提示信息,提示信息可以利用MFC中的CToolTipCtrl 类来实现,输出提示并不难,但是如何判断鼠标停留在某个按钮上呢?可以在对话框中响应WM_SETCOUSE消息,在响应事件中判断发生事件的窗口句柄是不是该控件句柄,如果是做出响应的操作即可,但是如果要判断光标离开控件,则相对复杂一点。因为光标离开控件后,接收鼠标移动消息的窗口句柄肯定不是该控件句柄了,也可以解决,如下:

BOOLCMy12Dlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)

{

         if(pWnd->GetDlgCtrlID() == IDC_BUTTON3)

         {

                   pWnd->SetWindowTextW(_T("Hover"));

         }

         else if(pWnd->GetDlgCtrlID() != IDC_BUTTON3)

         {

                   GetDlgItem(IDC_BUTTON3)->SetWindowTextW(_T("Leave"));

         }

         returnCDialogEx::OnSetCursor(pWnd, nHitTest, message);

}

(注:这样做,当鼠标快速的移动的时候,就有可能出现鼠标已经离开了按钮,但按钮还是显示鼠标停留在上面的现象。)

但是如果这样的控件不止一个的时候,难道要用nelse if去判断该句柄是不是对应的控件句柄吗?这样做就有点不合理了,有没有更好的解决办法呢?

经过试验,以下方法可行且相对来说最好:

Cbutton派生子类MyHoverBtn,在子类中响应WM_MOUSEMOVE事件,在该事件的响应函数中添加如下代码来响应鼠标的HoverLeave事件:

void MyHoverBtn::OnMouseMove(UINT nFlags, CPointpoint)

{

         TRACKMOUSEEVENT mouse_event;//定义鼠标移动事件结构体

         mouse_event.cbSize = sizeof(mouse_event);//定义结构体大小

         mouse_event.hwndTrack = m_hWnd;//关联窗口句柄

         mouse_event.dwFlags = TME_HOVER |TME_LEAVE;//设置响应标记

         mouse_event.dwHoverTime = 100/*HOVER_DEFAULT=400*/;//设置Hover的时间

         _TrackMouseEvent(&mouse_event);

 

         CButton::OnMouseMove(nFlags, point);

}

然后在子类MyHoverBtn中响应WM_MOUSEHOVERWM_MOUSELEAVE消息,并在相应的事件响应函数中添加实现代码;例如

void MyHoverBtn::OnMouseLeave()

{

         this->SetWindowTextW(_T("我离开了~"));

         CButton::OnMouseLeave();

}

 

 

void MyHoverBtn::OnMouseHover(UINT nFlags, CPointpoint)

{

         this->SetWindowTextW(_T("我来了~"));

         CButton::OnMouseHover(nFlags, point);

}

 

网上有人说可以在对话框中重载PreTranslateMessage函数,判断消息类型和控件句柄,如果消息是WM_MOUSEMOVE,且该事件发生的控件句柄就是你要控制的控件句柄,则做出响应的处理,代码如下:

BOOLCMy12Dlg::PreTranslateMessage(MSG* pMsg)

{

         CWnd* pWnd = GetDlgItem(IDC_BUTTON1);

         if(pMsg->hwnd == pWnd->GetSafeHwnd() && pMsg->message ==WM_MOUSEMOVE)

         {

                   CPoint point(pMsg->pt);

                   pWnd->ScreenToClient(&point);

                   CRect rect;

                   pWnd->GetClientRect(&rect);

                   if(rect.PtInRect(point))

                            pWnd->SetWindowTextW(_T("Hover"));

                   else

                            pWnd->SetWindowTextW(_T("Leave"));

                   returnTRUE;

         }

         returnCDialogEx::PreTranslateMessage(pMsg);

}

请注意绿色部分,这里不合理,因为当光标离开按钮控件后,鼠标移动的事件目标控件就不是该按钮了,与文章开头的方法是一样的,需要另外判断···

说了几种方式,还是派生子类,然后在子类中响应鼠标移动消息,从而做出对应操作最好。但是在设置Hover的时间的时候如果按照默认的HOVER_DEFAULT设置,也就是时间是400毫秒,那么鼠标移动到控件上面后需要等待这个时间才会响应OnMouseHover()于是我将时间减小为100毫秒,比较合理了,如下:

       mouse_event.dwHoverTime = 100/*HOVER_DEFAULT=400*/;//设置Hover的时间

就是不懂如果将时间设置到很小,会不会导致程序不断的提取鼠标移动消息,导致CPU占用增加呢?

经测试将时间设置为1毫秒,相对与设置为100毫秒,cpu也不会大幅增加,MSDN对该参数的解释如下:

dwHoverTime

Specifiesthe hover time-out (if TME_HOVER was specified in dwFlags), in milliseconds.Can be HOVER_DEFAULT, which means to use the system default hover time-out.

 

窗口重绘方面:

Invalidate()使窗口区域无效,以等待下一次重绘WM_PAINT消息将在消息队列为空时发送。与之类似的还有InvalidateRect(),可指定重绘具体的矩形区域。

UpdateWindow() 强制立即更新窗口,在OnPaint()函数中可以用GetUpdateRect()确定重绘区域或者判断是否需要重绘。

RedrawWindowUpdateWindow类似,强制重绘窗口,但是它提供了更多的参数以设置重绘的区域等

在重绘时,需调用BeginPaint()来准备好用于绘画的windows设备环境,填充 PAINTSTRUCT结构,并将DC句柄返回用于重绘。

绘画结束时,需调用EndPaint()来释放设备句柄。


0 0
原创粉丝点击