GUI02-在窗口上跟踪并输出鼠标位置(Win32版)

(1) 响应 WM_MOUSEMOVE 消息获得鼠标位置;

(2) 响应 WM_PAINT 将鼠标位置输出到窗口中;

(3) 学习二者之间的关键步骤:调用 InvalidateRect() 以通知窗口重绘。

零. 课堂视频

在窗口上跟踪输出鼠标位置-Win32版

一、关键知识点

1. BeginPaint() 对比 GetDC()

两个API都能得到指定窗口的 DC (设备上下文),供程序后续在 DC 上画图、输出文字等。二者区别在于,BeginPaint() 是在窗口收到 WM_PAINT 消息后使用。窗口收到 WM_PAINT 意味着:这个窗口当前展现的内容已经失效,确实需要重画。而 GetDC() 是直接获得一个窗口的DC,然后直接开画。

再往前推一步:一个程序有机会响应某个窗口的消息(比如这里的 WM_PAINT),通常就意味着这个窗口是该程序自己创建出来的。

二者在参数上也有明显不同,BeginPaint() 除窗口句柄外,还需要先创建一个 PAINTSTRCT 的变量。

2. EndPaint()

GetDC() 调用后,要记得配套地写上 ReleaseDC(),类似,我们从 BeginPaint() 得到一个 DC,在完成使用该 DC 之后,要记得配套调用 EndPaint()。

3. WM_MOUSEMOVE 消息

坐标点在该消息的响应函数的 lParam 参数,该参数的类型的是 LPARAM, 它是Windows开发库定义的一个宏。其中 L 表示 “long” ,也就是长整型。然而,在 Windows 上,哪怕是 64 位的系统,long 其实仍然一个 int ,因此还是四字节。当它用作 WM_MOUSEMOVE 的参数时,从左到右,前两个字节(低字),存储鼠标移动后的 x 坐标,高字存储 y 坐标。

对一个整数(int,long)取低字,可使用宏 LOWORD(),其中 LO 是 “low / 低” 的缩写,对应的取高字使用 HIWORD(), “HI” 是 “High / 高” 的缩写。

4. InvalidateRect

函数原型(声明)为:

BOOL InvalidateRect( HWND hWnd, const RECT *lpRect, BOOL bErase ); 

InvalidateRect 用于让指定窗口(hWnd)的指定区域(lpRect,说明见下)的绘图内容失效。

如果 lpRect 为空指针,则让整个窗口的(严格讲是客户区,即不包容标题栏、边框等)的绘图内容都失效。这样,窗口就会收到 WM_PAINT 消息,然后程序调用处理该消息的函数,开始重绘。

bErase 为真时,如果该窗口设置了合适的默认背景刷子(Brush,类似于 Pen,也是一种绘图资源 ), 系统将保证在重绘前,使用该刷子(特定颜色、样式),“刷掉” 原有内容。注意,窗口并不一定有合适的背景刷子。在我们的示例程序中,以下这一行保证了这一点:

/* Use Windows's default colour as the background of the window */

wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

其中的 RECT 是一个结构(struct),定义为:

struct RECT 
{ 
   long left, top, right, bottom;
}; 

即,通过 左、上、右、下 位置,定义一个矩形区域。

三、完整代码

#include <string>
#include <sstream> // stringstream

#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif

#include <tchar.h>
#include <windows.h>

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");

int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           _T("Code::Blocks Template Windows App"),       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}

int xPos, yPos; // 用于记录鼠标的坐标x和y值

LRESULT CALLBACK OnMouseMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    xPos = LOWORD(lParam); // 低字 是 x
    yPos = HIWORD(lParam); // 高字 是 y

    ::InvalidateRect(hwnd, nullptr, true); // true: 宣告窗口整个区域的现有展现内容失效,并且自动擦除

    return DefWindowProc (hwnd, message, wParam, lParam);
}

LRESULT CALLBACK OnPaint(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;

    HDC hDC = ::BeginPaint(hwnd, &ps);

    std::stringstream ss;
    ss << "[" << xPos << " | " << yPos << "]-Hello Win32-来自d2school的南老师";
    auto txt = ss.str();
    ::TextOut(hDC, xPos, yPos, txt.c_str(), txt.size());

    ::EndPaint(hwnd, &ps);

    return DefWindowProc (hwnd, message, wParam, lParam);
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
        case WM_MOUSEMOVE:
            return OnMouseMove(hwnd, message, wParam, lParam);
        case WM_PAINT :
            return OnPaint(hwnd, message, wParam, lParam);
        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

三、效果示例

效果示例

  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南郁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值