上篇文章中讲了如何创建一个简单的Win32的窗口,这篇文章简单讲一下如何响应鼠标和键盘事件。
当一个按键被按下时,Windows会向获得焦点得窗口所在得线程传递一个WM_KEYDOWN 或 WM_SYSKEYDOWN 消息。当释放这个按键时,Windows会发送一个WM_KEYUP 或 WM_SYSKEYUP 消息。WM_SYSKEYDOWN 和 WM_SYSKEYUP 是用户敲击系统键盘时产生得消息。默认情况下应该把他们交给系统 DefWindowProc 函数来处理。
在这几个消息中, wParam 包含了按键得虚拟键盘码,lParam 包含了另外一些状态信息。
当一个WM_KEYDOWN 消息被 TranslateMessage 函数转化后会有一个 WM_CHAR 消息产生,此消息得 wParam 参数包含了按键得ANSI码。例如当用户敲击键盘“A”键,窗口会一次收到下面三个消息:
下面是窗口函数处理消息 WM_KEYCHAR 将按下得键绘制到窗口上得代码:
LRESULT CALLBACK MainWindProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static std::wstring str;
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = ::BeginPaint(hwnd, &ps);
::TextOut(hdc, 0, 0, str.c_str(), str.size());
::EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
case WM_CHAR:
str += char(wParam);
::InvalidateRect(hwnd, nullptr, 0);
return 0;
}
return ::DefWindowProc(hwnd, message, wParam, lParam);
}
函数 InvalidateRect 会使客户区无效,迫使Windows再发送 WM_PAINT 消息,该函数原型为:
BOOL InvalidateRect (HWND hwnd, CONST RECT *lpRect, BOOL bErase)
- hwnd: 为窗口句柄
- lpRect: 指定无效区域得范围,如果为nullptr则为整个客户区
- bErase: 更新区域时,背景是否擦处。
BeginPaint 函数得第二个参数为一个指向 PAINTSTRUCT 结构得指针,此结构包含重画客户区时所需要得信息。
typedef struct tagPAINTSTRUCT {
HDC hdc; // 设备环境句柄
BOOL fErase; // 指定背景是否删除
RECT rcPaint; // 要求重画的区域
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;
当鼠标被按下或者移动等操作,windows会发送消息给窗口,下面是不同的操作发送不同的消息表格:
按下 | 弹起 | 双击 | |
---|---|---|---|
左键 | WM_LBUTTONDOWN | WM_LBUTTONUP | WM_LBUTTONDBCLCK |
中键 | WM_MBUTTONDOWN | WM_MBUTTONUP | WM_MBUTTONDBCLCK |
右键 | WM_RBUTTONDOWN | WM_RBUTTONUP | WM_RBUTTONDBCLCK |
发送消息时, lParam 参数包含了鼠标的坐标
xPos = LOWORD(lParam);
yPos = HIWORD(lParam);
客户区坐标和屏幕坐标的相互转化可以使用下面的函数:
- BOOL ClientToScreen (HWND hWnd, LPPOINT lpPoint)
- BOOL ScreenToClient (HWND hWnd, LPPOINT lpPoint)
wParam 参数包含鼠标的按钮的状态,这些都是以MK_ 前缀,意为 mouse key
下面是当左键按下时,绘制显示当时鼠标位置的窗口函数代码:
LRESULT CALLBACK MainWindProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static std::wstring str;
switch (message)
{
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = ::BeginPaint(hwnd, &ps);
::TextOut(hdc, 0, 0, str.c_str(), str.size());
::EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
case WM_LBUTTONDOWN:
WCHAR message[60];
memset(message, 0, sizeof(message));
wsprintf(message, L"X=%d, Y=%d", LOWORD(lParam), HIWORD(lParam));
str = message;
::InvalidateRect(hwnd, nullptr, FALSE);
return 0;
}
return ::DefWindowProc(hwnd, message, wParam, lParam);
}
如果想设置文字的背景色和文本颜色可以使用下面的函数:
SetTextColor(hdc, RGB(255, 0, 0)); // 文本颜色设置为红色
SetBkColor(hdc, RGB(0, 0, 255)); // 背景颜色设置为蓝色