VC++ 解决在鼠标移动时,光标闪烁的问题。其实本质是 ON_SETCURSOR的用法

时间:2022-10-07 07:57:21

在调用Windows API函数SetCursor设置光标时,可能会碰到闪烁的问题:移动鼠标,光标在Class Cursor(即注册窗口类时指定的Cursor)与预设Cursor之间闪烁。

在MSDN上有关SetCursor函数的备注中强调,如果Class Cursor非空,那么每当鼠标移动,系统都会把光标恢复为Class Cursor。为了避免光标闪烁这一问题,必须处理WM_SETCURSOR消息。(MSDN说明)

下面是一个例子:程序在主窗口视图的中间位置绘制RGB条带,当鼠标移动在条带范围就将光标设置成为Cross,此外根据光标的位置,在RGB条带上方30px处显示所处条带的颜色。程序运行起来像这样:

VC++ 解决在鼠标移动时,光标闪烁的问题。其实本质是 ON_SETCURSOR的用法

如果在WM_MOUSEMOVE的消息处理中判断光标的位置并设置光标的话,就会碰到所说的光标闪烁问题。WM_MOUSEMOVE的消息处理如下代码所示:

.LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
.{
. POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
. RECT rect, rectText;
. get_rects(&rect, &rectText);
. InvalidateRect(&rectText);
. UpdateWindow();
. if (::PtInRect(&rect, ptCursor)) {
. ::SetCursor(m_cursor);
. int dx = (rect.right - rect.left) / ;
. LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };
. int index;
. if (ptCursor.x - rect.left < dx)
. index = ;
. else if (ptCursor.x - rect.left < * dx)
. index = ;
. else index = ;
. WTL::CString str;
. str.Format(_T("Cursor on %s part"), ppsz[index]);
. CClientDC dc(m_hWnd);
. dc.DrawText(str, -, &rectText, DT_CENTER | DT_VCENTER);
. }
. else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));
. return ;
.}

闪烁产生的原因在于每次进入OnMouseMove之前,系统都会先将光标恢复成Arrow,进入OnMouseMove之后,如果光标处在RGB条带范围内则立即被设置成Cross。

解决办法就是将上面的判断逻辑放在WM_SETCURSOR的消息处理中,当然获得光标客户坐标的方式不同,代码如下所示:

[cpp] view plaincopyprint?
.LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
.{
. POINT point;
. ::GetCursorPos(&point);
. ScreenToClient(&point);
. set_cursor(point);
. return ;
.}
	LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
POINT point;
::GetCursorPos(&point);
ScreenToClient(&point);
set_cursor(point);
return 0;
}

而代码中的set_cursor私有方法其实就是上面的判断逻辑,即:

[cpp] view plaincopyprint?
.// ptCursor: in client coordinate
.void set_cursor(POINT& ptCursor) throw()
.{
. RECT rect, rectText;
. get_rects(&rect, &rectText);
. InvalidateRect(&rectText);
. UpdateWindow();
. if (::PtInRect(&rect, ptCursor)) {
. ::SetCursor(m_cursor);
. int dx = (rect.right - rect.left) / ;
. LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };
. int index;
. if (ptCursor.x - rect.left < dx)
. index = ;
. else if (ptCursor.x - rect.left < * dx)
. index = ;
. else index = ;
. WTL::CString str;
. str.Format(_T("Cursor on %s part"), ppsz[index]);
. CClientDC dc(m_hWnd);
. dc.DrawText(str, -, &rectText, DT_CENTER | DT_VCENTER);
. }
. else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));
.}
	// ptCursor: in client coordinate
void set_cursor(POINT& ptCursor) throw()
{
RECT rect, rectText;
get_rects(&rect, &rectText);
InvalidateRect(&rectText);
UpdateWindow();
if (::PtInRect(&rect, ptCursor)) {
::SetCursor(m_cursor);
int dx = (rect.right - rect.left) / 3;
LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };
int index;
if (ptCursor.x - rect.left < dx)
index = 0;
else if (ptCursor.x - rect.left < 2 * dx)
index = 1;
else index = 2;
WTL::CString str;
str.Format(_T("Cursor on %s part"), ppsz[index]);
CClientDC dc(m_hWnd);
dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);
}
else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));
}

这样就解决了光标闪烁的问题。

在调用Windows API函数SetCursor设置光标时,可能会碰到闪烁的问题:移动鼠标,光标在Class Cursor(即注册窗口类时指定的Cursor)与预设Cursor之间闪烁。

在MSDN上有关SetCursor函数的备注中强调,如果Class Cursor非空,那么每当鼠标移动,系统都会把光标恢复为Class Cursor。为了避免光标闪烁这一问题,必须处理WM_SETCURSOR消息。(MSDN说明)

下面是一个例子:程序在主窗口视图的中间位置绘制RGB条带,当鼠标移动在条带范围就将光标设置成为Cross,此外根据光标的位置,在RGB条带上方30px处显示所处条带的颜色。程序运行起来像这样:

VC++ 解决在鼠标移动时,光标闪烁的问题。其实本质是 ON_SETCURSOR的用法

如果在WM_MOUSEMOVE的消息处理中判断光标的位置并设置光标的话,就会碰到所说的光标闪烁问题。WM_MOUSEMOVE的消息处理如下代码所示:

  1. LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  2. {
  3. POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
  4. RECT rect, rectText;
  5. get_rects(&rect, &rectText);
  6. InvalidateRect(&rectText);
  7. UpdateWindow();
  8. if (::PtInRect(&rect, ptCursor)) {
  9. ::SetCursor(m_cursor);
  10. int dx = (rect.right - rect.left) / 3;
  11. LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };
  12. int index;
  13. if (ptCursor.x - rect.left < dx)
  14. index = 0;
  15. else if (ptCursor.x - rect.left < 2 * dx)
  16. index = 1;
  17. else index = 2;
  18. WTL::CString str;
  19. str.Format(_T("Cursor on %s part"), ppsz[index]);
  20. CClientDC dc(m_hWnd);
  21. dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);
  22. }
  23. else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));
  24. return 0;
  25. }
	LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
RECT rect, rectText;
get_rects(&rect, &rectText);
InvalidateRect(&rectText);
UpdateWindow();
if (::PtInRect(&rect, ptCursor)) {
::SetCursor(m_cursor);
int dx = (rect.right - rect.left) / 3;
LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };
int index;
if (ptCursor.x - rect.left < dx)
index = 0;
else if (ptCursor.x - rect.left < 2 * dx)
index = 1;
else index = 2;
WTL::CString str;
str.Format(_T("Cursor on %s part"), ppsz[index]);
CClientDC dc(m_hWnd);
dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);
}
else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));
return 0;
}

闪烁产生的原因在于每次进入OnMouseMove之前,系统都会先将光标恢复成Arrow,进入OnMouseMove之后,如果光标处在RGB条带范围内则立即被设置成Cross。

解决办法就是将上面的判断逻辑放在WM_SETCURSOR的消息处理中,当然获得光标客户坐标的方式不同,代码如下所示:

  1. LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
  2. {
  3. POINT point;
  4. ::GetCursorPos(&point);
  5. ScreenToClient(&point);
  6. set_cursor(point);
  7. return 0;
  8. }
	LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
POINT point;
::GetCursorPos(&point);
ScreenToClient(&point);
set_cursor(point);
return 0;
}

而代码中的set_cursor私有方法其实就是上面的判断逻辑,即:

  1. // ptCursor: in client coordinate
  2. void set_cursor(POINT& ptCursor) throw()
  3. {
  4. RECT rect, rectText;
  5. get_rects(&rect, &rectText);
  6. InvalidateRect(&rectText);
  7. UpdateWindow();
  8. if (::PtInRect(&rect, ptCursor)) {
  9. ::SetCursor(m_cursor);
  10. int dx = (rect.right - rect.left) / 3;
  11. LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };
  12. int index;
  13. if (ptCursor.x - rect.left < dx)
  14. index = 0;
  15. else if (ptCursor.x - rect.left < 2 * dx)
  16. index = 1;
  17. else index = 2;
  18. WTL::CString str;
  19. str.Format(_T("Cursor on %s part"), ppsz[index]);
  20. CClientDC dc(m_hWnd);
  21. dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);
  22. }
  23. else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));
  24. }
	// ptCursor: in client coordinate
void set_cursor(POINT& ptCursor) throw()
{
RECT rect, rectText;
get_rects(&rect, &rectText);
InvalidateRect(&rectText);
UpdateWindow();
if (::PtInRect(&rect, ptCursor)) {
::SetCursor(m_cursor);
int dx = (rect.right - rect.left) / 3;
LPTSTR ppsz[] = { _T("Red"), _T("Green"), _T("Blue") };
int index;
if (ptCursor.x - rect.left < dx)
index = 0;
else if (ptCursor.x - rect.left < 2 * dx)
index = 1;
else index = 2;
WTL::CString str;
str.Format(_T("Cursor on %s part"), ppsz[index]);
CClientDC dc(m_hWnd);
dc.DrawText(str, -1, &rectText, DT_CENTER | DT_VCENTER);
}
else ::SetCursor(CCursor().LoadSysCursor(IDC_ARROW));
}

这样就解决了光标闪烁的问题。

VC++ 解决在鼠标移动时,光标闪烁的问题。其实本质是 ON_SETCURSOR的用法的更多相关文章

  1. VC&plus;&plus;大数据量绘图时无闪烁刷屏技术实现(我的理解是,在内存上作画,然后手动显示,而不再直接需要经过WM&lowbar;PAINT来处理了)

    http://hantayi.blog.51cto.com/1100843/383578 引言 当我们需要在用户区显示一些图形时,先把图形在客户区画上,虽然已经画好但此时我们还无法看到,还要通过 程序 ...

  2. 【ios bug解决】 输入框聚焦时光标不显示

    解决办法:重写user-select样式 css:   user-select: text;-webkit-user-select:text;

  3. C&num;中的WinForm问题——使用滚动条时页面闪烁及重影问题

    当使用鼠标进行滚动查看页面时,由于页面会频繁刷新,如果页面中控件较多会导致页面出现闪烁.重影等问题,如下图所示: 在网上搜索过该问题,大部分都说使用双缓冲可以解决此类问题,即通过设置DoubleBuf ...

  4. VC&sol;MFC 当鼠标移到控件上时显示提示信息

    VC/MFC 当鼠标移到控件上时显示提示信息 ToolTip是Win32中一个通用控件,MFC中为其生成了一个类CToolTipCtrl,总的说来其使用方法是较简单的,下面讲一下它的一般用法和高级用法 ...

  5. 通过特殊处理 Resize 事件解决 WinForm 加载时闪烁问题的一个方法

    WinForm 上放置的控件多了或者有大背景图,窗体加载时就会闪烁,对于一般的闪烁,设置 DoubleBuffer=True或许有一点改善,要立竿见影的解决可以重载 CreateParams 使用 W ...

  6. ListView用法及加载数据时的闪烁问题和加载数据过慢问题

    ListView介绍及添加数据时的闪烁问题 1.     ListView类 1.1 ListView常用的基本属性: (1)FullRowSelect:设置是否行选择模式.(默认为false) 提示 ...

  7. 使用mx&colon;Repeater在删除和添加item时列表闪烁

    使用mx:Repeater在删除和添加item时列表闪烁 不可能在用户界面上闪闪的吧,recycleChildren属性可帮助我们 recycleChildren属性==缓存,设为true就可以了 本 ...

  8. 防止vuejs在解析时出现闪烁

    ---## 防止vuejs在解析时出现闪烁 ## 原因: 在使用vuejs.angularjs开发时,经常会遇见在如Chrome这类能够快速解析的浏览器上出现表达式({{ express }} ),或 ...

  9. jQuery的鼠标悬停时放大图片的效果

    这是一个基于jQuery的效果,当鼠标在小图片上悬停时,会弹出一个大图,该大图会跟随鼠标的移动而移动.这个效果最初源于小敏同志的一个想法,刚开始做的时候只能实现弹出的图片是固定的,不能随鼠标移动,最后 ...

随机推荐

  1. asp&period;net下ajax&period;ajaxMethod使用方法

    使用AjaxMethod可以在客户端异步调用服务端方法,简单地说就是在JS里调用后台.cs文件里的方法,做一些JS无法做到的操作,如查询数据库.   使用AjaxMethod要满足一下几点: 1.如果 ...

  2. hdu 4300 Clairewd’s message KMP应用

    Clairewd’s message 题意:先一个转换表S,表示第i个拉丁字母转换为s[i],即a -> s[1];(a为明文,s[i]为密文).之后给你一串长度为n<= 100000的前 ...

  3. Switch Case语句中多个值匹配同一个代码块的写法

    switch ($p) { case 'home': case '': $current_home = 'current'; break; case 'users.online': case 'use ...

  4. qwt的安装与使用

    qwt简介 QWT,全称是Qt Widgets for Technical Applications,是一个基于LGPL版权协议的开源项目, 可生成各种统计图. 具体介绍,可参看官方网址:http:/ ...

  5. Android--UI之Fragment

    前言 开门见山开篇名义,本篇博客将讲解一下Android中Fragment的内容,必要的地方会提供相应的演示代码,并且会在最后给出源码下载. 本文主要有以下内容: 什么是Fragment 如何创建一个 ...

  6. VI和VIM

    linux下vi.VIM命令大全   进入vi的命令 vi filename :打开或新建文件,并将光标置于第一行首 vi +n filename :打开文件,并将光标置于第n行首 vi + file ...

  7. day27 软件开发规范&comma;以及面相对象回顾

    面向对象所有内容回顾: # 面向对象 # 类 :一类具有相同属性和方法的事物 #类的定义:class #类中可以定义的方法种类: #普通方法 self 对象 #类方法 cls @classmethod ...

  8. iOS开发SDWebImageOptions理解

    iOS开发SDWebImageOptions理解 原文 http://www.cnblogs.com/WJJ-Dream/p/5816750.html typedef NS_OPTIONS(NSUIn ...

  9. Python里面这些点,据说80&percnt;的新手都会一脸懵逼

    Python里面这些点,据说80%的新手都会一脸懵逼 菜鸟Python 关注 2018.10.10 12:51 字数 1833 阅读 123评论 0喜欢 10 Python虽然语法简单,通俗易懂,但是 ...

  10. SpringBoot学习(四)

    spring boot 默认端口是 8080,如果想要进行更改的话,只需要修改 application.properties 文件,在配置文件中加入: 1. server.port=9090 其他常用 ...