Windows桌面应用程序(1-2-5-3rd) 鼠标移动
2018-02-02 20:40
721 查看
当鼠标移动时,Windows发布WM_MOUSEMOVE消息。默认情况下,WM_MOUSEMOVE进入包含光标的窗口。您可以通过捕获鼠标来覆盖此行为,这将在下一节中介绍。
WM_MOUSEMOVE消息包含与鼠标点击消息相同的参数。lParam的最低16位包含x坐标,接下来的16位包含y坐标。使用GET_X_LPARAM和GET_Y_LPARAM宏从lParam解开坐标。wParam参数包含标志的按位OR,指示其他鼠标按钮加SHIFT和CTRL键的状态。以下代码从lParam获取鼠标坐标。
请记住,这些坐标是以像素为单位,而不是与设备无关的像素(DIP)。在这个主题的后面,我们将看看在这两个单元之间转换的代码。
如果光标位置相对于窗口改变,窗口也可以接收WM_MOUSEMOVE消息。例如,如果光标位于窗口之上,并且用户隐藏窗口,则即使鼠标没有移动,窗口也会收到WM_MOUSEMOVE消息。此行为的一个后果是鼠标坐标可能不会更改WM_MOUSEMOVE消息之间。
在窗外捕捉鼠标移动
默认情况下,如果鼠标移过客户区边缘,窗口将停止接收WM_MOUSEMOVE消息。但是对于某些操作,您可能需要跟踪超出此点的鼠标位置。例如,绘图程序可以使用户将选择矩形拖到窗口边缘之外,如下图所示。
鼠标捕捉的例证。
要通过窗口边缘接收鼠标移动消息,请调用SetCapture函数。调用此函数后,即使鼠标移出窗口,只要用户至少按住一个鼠标按钮,窗口也将继续接收WM_MOUSEMOVE消息。捕捉窗口必须是前景窗口,一次只能有一个窗口成为捕捉窗口。要释放鼠标捕获,请调用ReleaseCapture函数。
您通常会以下面的方式使用SetCapture和ReleaseCapture。
当用户按下鼠标左键时,调用SetCapture开始捕捉鼠标。
回应鼠标移动的消息。
当用户释放鼠标左键时,调用ReleaseCapture。
示例:绘图圈
让我们从模块3扩展Circle程序,让用户用鼠标画一个圆。从Direct2D Circle Sample程序开始。我们将修改此示例中的代码以添加简单的绘图。首先,向MainWindow类添加一个新的成员变量。
当用户拖动鼠标时,该变量存储鼠标向下的位置。在MainWindow构造函数中,初始化ellipse和ptMouse变量。
删除MainWindow::CalculateLayout方法的主体;这个例子不是必需的。
接下来,声明左侧按钮,左侧按钮和鼠标移动消息的消息处理程序。
鼠标坐标以物理像素为单位,但Direct2D需要与设备无关的像素(DIP)。要正确处理高DPI设置,必须将像素坐标转换为DIP。有关DPI的更多讨论,请参阅DPI和设备无关的像素。以下代码显示了将像素转换为DIP的辅助类。
在创建Direct2D工厂对象后,请在您的WM_CREATE处理程序中调用DPIScale::Initialize。
要从鼠标消息中获取DIP中的鼠标坐标,请执行以下操作:
使用GET_X_LPARAM和GET_Y_LPARAM宏来获取像素坐标。这些宏是在WindowsX.h中定义的,所以记得在你的项目中包含这个头文件。
调用DPIScale::PixelsToDipsX和DPIScale::PixelsToDipsY将像素转换为DIP。
现在将消息处理程序添加到您的窗口过程。
最后,自己实现消息处理程序。
左键按下
对于左键按下消息,请执行以下操作:
调用SetCapture开始捕捉鼠标。
将鼠标点击的位置存储在ptMouse变量中。该位置定义椭圆的边界框的左上角。
重置椭圆结构。
调用InvalidateRect。该功能强制窗口被重新粉刷。
鼠标移动
对于鼠标移动消息,检查鼠标左键是否关闭。如果是,重新计算椭圆并重新绘制窗口。在Direct2D中,椭圆由中心点和x和y半径定义。我们要绘制一个适合由鼠标点(ptMouse)和当前光标位置(x,y)定义的边界框的椭圆,所以需要一些算术来找到椭圆的宽度,高度和位置。
以下代码重新计算椭圆,然后调用InvalidateRect重新绘制窗口。
左键松开
对于左键消息,只需调用ReleaseCapture即可释放鼠标捕获。
下一个
其他鼠标操作
WM_MOUSEMOVE消息包含与鼠标点击消息相同的参数。lParam的最低16位包含x坐标,接下来的16位包含y坐标。使用GET_X_LPARAM和GET_Y_LPARAM宏从lParam解开坐标。wParam参数包含标志的按位OR,指示其他鼠标按钮加SHIFT和CTRL键的状态。以下代码从lParam获取鼠标坐标。
int xPos=GET_X_LPARAM(lParam); int yPos=GET_Y_LPARAM(lParam);
请记住,这些坐标是以像素为单位,而不是与设备无关的像素(DIP)。在这个主题的后面,我们将看看在这两个单元之间转换的代码。
如果光标位置相对于窗口改变,窗口也可以接收WM_MOUSEMOVE消息。例如,如果光标位于窗口之上,并且用户隐藏窗口,则即使鼠标没有移动,窗口也会收到WM_MOUSEMOVE消息。此行为的一个后果是鼠标坐标可能不会更改WM_MOUSEMOVE消息之间。
在窗外捕捉鼠标移动
默认情况下,如果鼠标移过客户区边缘,窗口将停止接收WM_MOUSEMOVE消息。但是对于某些操作,您可能需要跟踪超出此点的鼠标位置。例如,绘图程序可以使用户将选择矩形拖到窗口边缘之外,如下图所示。
鼠标捕捉的例证。
要通过窗口边缘接收鼠标移动消息,请调用SetCapture函数。调用此函数后,即使鼠标移出窗口,只要用户至少按住一个鼠标按钮,窗口也将继续接收WM_MOUSEMOVE消息。捕捉窗口必须是前景窗口,一次只能有一个窗口成为捕捉窗口。要释放鼠标捕获,请调用ReleaseCapture函数。
您通常会以下面的方式使用SetCapture和ReleaseCapture。
当用户按下鼠标左键时,调用SetCapture开始捕捉鼠标。
回应鼠标移动的消息。
当用户释放鼠标左键时,调用ReleaseCapture。
示例:绘图圈
让我们从模块3扩展Circle程序,让用户用鼠标画一个圆。从Direct2D Circle Sample程序开始。我们将修改此示例中的代码以添加简单的绘图。首先,向MainWindow类添加一个新的成员变量。
D2D1_POINT_2F ptMouse;
当用户拖动鼠标时,该变量存储鼠标向下的位置。在MainWindow构造函数中,初始化ellipse和ptMouse变量。
MainWindow():pFactory(NULL),pRenderTarget(NULL),pBrush(NULL),ellipse(D2D1::Ellipse(D2D1::Point2F(),0,0)),ptMouse(D2D1::Point2F()){}
删除MainWindow::CalculateLayout方法的主体;这个例子不是必需的。
void CalculateLayout(){}
接下来,声明左侧按钮,左侧按钮和鼠标移动消息的消息处理程序。
void OnLButtonDown(int pixelX,int pixelY,DWORD flags); void OnLButtonUp(); void OnMouseMove(int pixelX,int pixelY,DWORD flags);
鼠标坐标以物理像素为单位,但Direct2D需要与设备无关的像素(DIP)。要正确处理高DPI设置,必须将像素坐标转换为DIP。有关DPI的更多讨论,请参阅DPI和设备无关的像素。以下代码显示了将像素转换为DIP的辅助类。
class DPIScale{ static float scaleX; static float scaleY; public: static void Initialize(ID2D1Factory *pFactory){ FLOAT dpiX,dpiY; pFactory->GetDesktopDpi(&dpiX,&dpiY); scaleX=dpiX/96.0f; scaleY=dpiY/96.0f; } template<typename T> static D2D1_POINT_2F PixelsToDips(T x,T y){ return D2D1::Point2F(static_cast<float>(x)/scaleX,static_cast<float>(y)/scaleY); } }; float DPIScale::scaleX=1.0f; float DPIScale::scaleY=1.0f;
在创建Direct2D工厂对象后,请在您的WM_CREATE处理程序中调用DPIScale::Initialize。
case WM_CREATE: if(FAILED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,&pFactory))) return -1;// Fail CreateWindowEx. DPIScale::Initialize(pFactory); return 0;
要从鼠标消息中获取DIP中的鼠标坐标,请执行以下操作:
使用GET_X_LPARAM和GET_Y_LPARAM宏来获取像素坐标。这些宏是在WindowsX.h中定义的,所以记得在你的项目中包含这个头文件。
调用DPIScale::PixelsToDipsX和DPIScale::PixelsToDipsY将像素转换为DIP。
现在将消息处理程序添加到您的窗口过程。
case WM_LBUTTONDOWN: OnLButtonDown(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),(DWORD)wParam); return 0; case WM_LBUTTONUP: OnLButtonUp(); return 0; case WM_MOUSEMOVE: OnMouseMove(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam),(DWORD)wParam); return 0;
最后,自己实现消息处理程序。
左键按下
对于左键按下消息,请执行以下操作:
调用SetCapture开始捕捉鼠标。
将鼠标点击的位置存储在ptMouse变量中。该位置定义椭圆的边界框的左上角。
重置椭圆结构。
调用InvalidateRect。该功能强制窗口被重新粉刷。
void MainWindow::OnLButtonDown(int pixelX,int pixelY,DWORD flags){ SetCapture(m_hwnd); ellipse.point=ptMouse=DPIScale::PixelsToDips(pixelX,pixelY); ellipse.radiusX=ellipse.radiusY=1.0f; InvalidateRect(m_hwnd,NULL,FALSE); }
鼠标移动
对于鼠标移动消息,检查鼠标左键是否关闭。如果是,重新计算椭圆并重新绘制窗口。在Direct2D中,椭圆由中心点和x和y半径定义。我们要绘制一个适合由鼠标点(ptMouse)和当前光标位置(x,y)定义的边界框的椭圆,所以需要一些算术来找到椭圆的宽度,高度和位置。
以下代码重新计算椭圆,然后调用InvalidateRect重新绘制窗口。
void MainWindow::OnMouseMove(int pixelX,int pixelY,DWORD flags){ if(flags&MK_LBUTTON){ const D2D1_POINT_2F dips=DPIScale::PixelsToDips(pixelX,pixelY); const float width=(dips.x-ptMouse.x)/2; const float height=(dips.y-ptMouse.y)/2; const float x1=ptMouse.x+width; const float y1=ptMouse.y+height; ellipse=D2D1::Ellipse(D2D1::Point2F(x1,y1),width,height); InvalidateRect(m_hwnd,NULL,FALSE); } }
左键松开
对于左键消息,只需调用ReleaseCapture即可释放鼠标捕获。
void MainWindow::OnLButtonUp(){ ReleaseCapture(); }
下一个
其他鼠标操作
相关文章推荐
- Windows桌面应用程序(1-2-2-3rd) 编写窗口过程
- Windows桌面应用程序(1-2-5-2nd) 响应鼠标点击
- Windows桌面应用程序(1-2-5-4th) 其他鼠标操作
- Windows桌面应用程序(1-1-2-3rd) 使用着色器和着色器资源
- 编写一个windows应用程序,要求在窗口的用户区中绘制一个圆 ,当单击左键时,该圆放大;单击右键时,该圆缩小;按下ctrl键时的同时鼠标移动,则该圆会随鼠标移动而移动
- Windows桌面应用程序(1-2-3rd) 模块2.在Windows程序中使用COM
- Windows桌面应用程序(1-2-5-1st) 鼠标输入
- Windows桌面应用程序(1-2-3-3rd) COM中的错误代码
- windows桌面频繁截图时的鼠标闪烁问题
- C#编写Windows桌面应用程序读取执行文件当前路径
- 远程桌面登录Windows 7 时报Atbroker.exe 应用程序错误
- Windows桌面应用程序(1-2-4th) 模块3.Windows图形
- Windows桌面应用程序(1-2-3-4th) 在COM中创建一个对象
- Windows服务无法启动桌面应用程序(Winform界面)
- KVM VNC Windows桌面 鼠标不同步
- C#编写Windows桌面应用程序窗口全屏显示
- 怎么实现“鼠标穿透”,即鼠标对窗体失去作用,对着它点右键要出现WINDOWS的桌面右菜单
- 电脑开机后原来桌面的图标全都没了,鼠标可以移动但右击不管用
- Windows桌面应用程序(1-2-3-5th) 例如:打开对话框
- Windows桌面应用程序(1-2-3-9th) COM编码实践