ShowCursor、MouseHover、MouseLeave使用与MFC框架理解

来源:互联网 发布:js调用java代码 编辑:程序博客网 时间:2024/05/01 09:49

又是在做计算机图形学大作业时遇到的问题,特此记录备忘~
这次想实现的功能是按下ESC键关闭窗口,以及在渲染窗口里隐藏光标,用图片代替光标位置,窗口其他位置显示默认的光标。

实现效果

这是我目前的工程文件,子窗口就是RenderView那个类。

因为搞不懂MFC默认生成的View类和MainFrame类到底管的是窗口的哪部分,所以我打算把鼠标响应和按键的事件加到这两个类试试。加到MainFrm里,调试发现根本就不响应。加到View类里也是如此。在论坛里提问,有人说

“MainFrame的区域只包括那些边边角角的地方,不包括视图、菜单、工具条、状态条等等……”

,所以不应该在MainFrm里面添加。而View类不响应却没人知道为什么。咨询老师之后,老师说可能是不小心删掉了什么默认生成的代码,所以才不响应。于是我新建了一个MFC,什么都不做,只在View类里添加了KEYDOWN的响应,一运行,成功到达断点位置!我那个奇怪啊,我记得我原来的那个程序根本就没动MFC默认生成的代码啊。但是响应成功就是目的,于是我就打算把原来程序的代码移植到新的“干净的”MFC程序内。但是,移植过来以后发现又不响应了。我思考过后认为,应该是wndSplitter划分子窗口时添加了自定义的RenderView类,因此默认的View类就不再响应,感觉像是被“替换”了。那默认的View类又有什么用呢?我想干脆把RenderView类的代码移到默认View类里,然后在wndSplitter的CreateClient里添加默认View类为右子窗口。但是VC给我报了奇怪的错误,定位在默认View里的GetDocument函数那,天知道为什么在那报错……!咨询老师后,老师说

wndSplitter的RUNTIME_CLASS那里添加的类必须是动态的类,而默认生成的View类是静态类,如果想要添加的话,需要在默认View的头文件里添加一行宏:DECLARE_DYNCREATE(CWorldEditorver10View),这样就把默认View类声明为一个动态类。

不过后来我并没有这么做,还是保留了RenderScene类,毕竟在默认生成的View类里加宏感觉还是虚虚的。ESC按键的问题通过论坛高人指点

可以在 App PreTranslateMessage 中截获消息

解决了,方法是类向导->CWorldEditorver10App->虚函数->PreTranslateMessage,添加后这样写:

BOOL CWorldEditorver10App::PreTranslateMessage(MSG* pMsg){    // TODO: 在此添加专用代码和/或调用基类    if(pMsg->wParam==VK_ESCAPE)    {        m_pMainWnd->PostMessage(WM_CLOSE);    }    return CWinAppEx::PreTranslateMessage(pMsg);}

这样就实现了在不论焦点在哪,按下ESC键都会关闭窗口了。
但是鼠标隐藏出现的事件一直有问题,我一开始得知有PreTranslateMessage这么开挂的函数后,想把鼠标响应事件也添加过来,像这样:

BOOL CWorldEditver10App::PreTranslateMessage(MSG* pMsg){    // TODO: 在此添加专用代码和/或调用基类    if(pMsg->wParam==VK_ESCAPE)    {        m_pMainWnd->PostMessage(WM_CLOSE);    }    //下面被注释掉的就是一开始我想用的隐藏光标的函数    //if(pMsg->message==WM_MOUSEMOVE)    //{    //  RECT rect;    //  CMainFrame *pFrame = (CMainFrame *)m_pMainWnd;    //  CView *pView = (CView *)pFrame->m_wndSplitter.GetPane(0,1);    //  CToolBar *pBar=(CToolBar *)pFrame->GetMenuBar();    //  pView->GetWindowRect(&rect);    //  if(pMsg->pt.x>=rect.left&&pMsg->pt.x<=rect.right&&pMsg->pt.y>=rect.top&&pMsg->pt.y<=rect.bottom)    //  {    //      while(!ShowCursor(FALSE));    //  }    //  else    //  {    //      while(!ShowCursor(TRUE));    //  }    //}    return CWinAppEx::PreTranslateMessage(pMsg);}

但是添加之后发现,的确能够实现光标隐藏和消失,但是!鼠标并没有如我所想的那样移进子窗口就消失,移出就出现!往往需要在移入/出之后晃晃鼠标才能消失/出现!调试查看获得的窗口坐标以及光标位置,数据一切正确,并不存在问题。因此我去查了ShowCursor的函数解释,MSDN是这样告诉我的:

ShowCursor
The ShowCursor function displays or hides the cursor.

int ShowCursor(  BOOL bShow   // cursor visibility);

Parameters
bShow
[in] Specifies whether the internal display counter is to be incremented or decremented. If bShow is TRUE, the display count is incremented by one. If bShow is FALSE, the display count is decremented by one.
Return Values
The return value specifies the new display counter.

Remarks
This function sets an internal display counter that determines whether the cursor should be displayed. The cursor is displayed only if the display count is greater than or equal to 0. If a mouse is installed, the initial display count is 0. If no mouse is installed, the display count is –1.

妈蛋!敢情ShowCursor居然是累积到一定程度才会实现消失/隐藏,根本不是字面意思看过去的给个TRUE or FALSE就能实现的!虽然不知道API设计成这么文不对题是出于什么目的,但是我起码可以用ShowCursor的返回值(就是那个计数器的当前值啦)来判断是否应该继续ShowCursor。而老师提示我其实可以不通过坐标判断这么麻烦的方法,MFC本身就提供了MouseHover、MouseLeave这样的消息响应。于是我来到了RenderScene类内,添加了这两个消息响应……但是并没有什么卵用QAQ。调试后根本不到这两处的断点那去。那么这两个函数的调用条件是什么呢?
再来求助MSDN吧~

The WM_MOUSEHOVER message is posted to a window when the cursor hovers over the client area of the window for the period of time specified in a prior call to
TrackMouseEvent.

MouseLeave用法也是这样。也就是说,光调用MouseHover、MouseLeave是不管用的,必须有TrackMouseEvent才能激活这两个函数响应。而且这种激活还是一次性的,如下:

Remarks
Hover tracking stops when WM_MOUSEHOVER is generated. The application must call TrackMouseEvent again if it requires further tracking of mouse hover behavior.

Use the following code to obtain the horizontal and vertical position:

因此结合ShowCursor和这俩函数的特性,最终的代码如下:

void CRenderScene::OnMouseHover(UINT nFlags, CPoint point){    // TODO: 在此添加消息处理程序代码和/或调用默认值    //只要返回的count值大于等于0,就不会显示鼠标    //因此只要判断返回值与0的关系,直到隐藏了才结束这个空循环    while(ShowCursor(FALSE)>=0);    CView::OnMouseHover(nFlags, point);}void CRenderScene::OnMouseLeave(){    // TODO: 在此添加消息处理程序代码和/或调用默认值    //只要返回的count值小于0,就不会显示鼠标    //因此只要判断返回值与0的关系,直到显示了才结束这个空循环    while(ShowCursor(TRUE)<0);    CView::OnMouseLeave();}void CRenderScene::OnMouseMove(UINT nFlags, CPoint point){    // TODO: 在此添加消息处理程序代码和/或调用默认值    TRACKMOUSEEVENT tme;      tme.cbSize = sizeof(tme);      tme.hwndTrack = m_hWnd;      tme.dwFlags = TME_LEAVE|TME_HOVER;      tme.dwHoverTime = 1;      _TrackMouseEvent(&tme);      m_opengl->GetCursorPos(point);//这个函数是干其他事儿的,勿理    //此处千万注意,不是InvalidateRect(FALSE),如果写成那样会有闪烁现象,看起来就像没开双缓冲似的    Invalidate(FALSE);    CView::OnMouseMove(nFlags, point);}

这样我就实现了十分爽利的光标移入移出消失出现的效果~
磨了老子一下午+一晚上+半个上午的时间……不过解决问题的成就感和解决问题耗用的时间成正比,看来我还得继续发掘更多BUG才行啊=v=

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 工厂破产了工资怎么办 失业就业登记证怎么办? 如果父母是教师怎么办 老人档案丢了怎么办 想离婚想要孩子怎么办 一二年级成绩差怎么办 高考复读又失败怎么办 孩子不想估成绩怎么办 手机信号被屏蔽了怎么办 西安市旅游年卡怎么办 广电宽带网速慢怎么办 电视智能卡坏了怎么办 家乐卡二次授信怎么办? 邢台银行倒闭了怎么办 不敢和导师交流怎么办 考上一个破大学怎么办 腻子粉检测报告怎么办 电子厂插件很慢怎么办 预付费电表跳闸怎么办 电费有疑问怎么办大连 农村电表箱坏了怎么办 农村电表没电怎么办 家里电费特别高怎么办 电表读卡失败怎么办 电表卡消磁了怎么办 智能表采集失败怎么办 电脑没有蓝牙功能怎么办 判决后无力偿还怎么办 dz47-63c63跳闸怎么办 租房合同丢了怎么办 北京土地承包合同丢失怎么办 租赁合同丢失了怎么办 房屋租赁合同丢失怎么办 学校没发学生证怎么办 学校银行卡丢了怎么办 报税名字忘了怎么办 地税零申报漏报怎么办 欠中联重科施工电梯钱怎么办 大型船舶起锚正横后怎么办 老师不会教孩子怎么办 个体工商营业证怎么办