客戶區滑鼠訊息、非客戶區滑鼠訊息、擊中測試、滑鼠滾輪
注:以下內容為學習筆記,多數是從書本、資料中得來,只為加深印象,及日後參考。然而本人表達能力較差,寫的不好。因非翻譯、非轉載,只好選原創,但多數乃摘抄,實為慚愧。但若能幫助一二訪客,幸甚!
注:以下內容多數摘自《Windows程式設計》
1. 滑鼠資訊
判斷是否連線了滑鼠
fMouse = GetSystemMetrics(SM_MOUSEPRESENT);
滑鼠按鈕個數
cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
2.客戶區滑鼠訊息
windows只把鍵盤訊息傳送到當前具有輸入焦點的視窗。滑鼠訊息則不同:當滑鼠經過視窗或在視窗內被單擊,則即使視窗是非活動視窗或不帶輸入焦點,視窗過程還是會收到滑鼠訊息。Windows定義了21種滑鼠訊息。其中11種訊息與客戶區無關,稱為“非客戶區訊息”。
滑鼠訊息,引數lParam包含了滑鼠的位置資訊,低位位元組表示x座標,高位位元組表示y座標,它們都是相對於視窗客戶區左上角的相對座標。
x = LOWORD(lParam);
y = HIWORD(lParam);
引數wParam表示滑鼠按鈕、Shift鍵和Ctrl鍵的狀態。如:
wParam & MK_SHIFT 非零,表示按下左鍵同時按下了Shift鍵。
若在非活動視窗的客戶區內按下滑鼠左鍵,Windows會將該視窗變為活動視窗,並向視窗過程傳送WM_LBUTTONDOWN訊息。
當用戶在其他視窗內按下滑鼠,再移動到使用者視窗,然後釋放,次數就會發生使用者視窗沒有收到WM_LBUTTONDOWN但收到了WM_LBUTTONUP訊息。
滑鼠處理示例:
/***************************************************************************** connect.cpp -- Connect-the-Dots Mouse Demo Program *****************************************************************************/ #include <windows.h> #define MAXPOINTS 1000 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT("Connect"); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style = CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc = WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires windows NT!"), szAppName, MB_ICONERROR); return 0; } hwnd = CreateWindow(szAppName, // window class name TEXT("Connect-the-Points Mouse Demo"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL); // creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static POINT pt[MAXPOINTS]; static int iCount; int i, j; HDC hdc; PAINTSTRUCT ps; switch (message) { case WM_LBUTTONDOWN: iCount = 0; InvalidateRect(hwnd, NULL, TRUE); return 0; case WM_MOUSEMOVE: if (wParam & MK_LBUTTON && iCount < 1000) { pt[iCount].x = LOWORD(lParam); pt[iCount].y = HIWORD(lParam); iCount++; hdc = GetDC(hwnd); SetPixel(hdc, LOWORD(lParam), HIWORD(lParam), 0); ReleaseDC(hwnd, hdc); } return 0; case WM_LBUTTONUP: InvalidateRect(hwnd, NULL, FALSE); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); SetCursor(LoadCursor(NULL, IDC_WAIT)); ShowCursor(TRUE); for (i = 0; i < iCount-1; i++) for (j = i+1; j < iCount; j++) { MoveToEx(hdc, pt[i].x, pt[i].y, NULL); LineTo(hdc, pt[j].x, pt[j].y); } ShowCursor(FALSE); SetCursor(LoadCursor(NULL, IDC_ARROW)); EndPaint(hwnd, &ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }
3.處理Shift鍵
if (wParam & MK_SHIFT)
{
if (wParam & MK_CONTROL)
{
// 按下Shift+Ctrl組合鍵
}
else
{
// 按下Shift鍵
{
}
else
{
if (wParam & MK_CONTROL)
{
// 按下Ctrl鍵
}
else
{
// Shift 和Ctrl 都沒按下
}
}
4.滑鼠雙擊
如果想讓視窗過程接收滑鼠雙擊訊息,RegisterClass中必須在視窗風格欄位中包含標示符CS_DBLCLKS:
wndclass.style = CS_HREDRAW | CS_VERDRAW | CS_DBLCLKS;
5.非客戶區滑鼠訊息
視窗的非客戶區包括標題欄、選單和視窗滾動條。
非客戶區訊息的wParam表示非客戶區滑鼠移動或單擊的位置。lParam的低位字包含x座標、高位字包含y座標。但是這些座標是螢幕座標,而不是前面的客戶區訊息中的客戶區座標。
這兩種座標的相互轉化:
ScreenToClient(hwnd, &pt);
ClientToScreen(hwnd, &pt);
6.擊中測試
擊中測試包含了對傳遞到視窗過程的x和y座標的一些運算,其中x和y的值包含在滑鼠訊息的引數lParam中。
簡單示例:
/*****************************************************************************
checker.cpp -- Mouse Hit-Test Demo Program ver1
*****************************************************************************/
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Checker1");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, // window class name
TEXT("Checker1 Mouse Hit-Test Demo"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static BOOL fState[DIVISIONS][DIVISIONS];
static int cxBlock, cyBlock;
int x, y;
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_SIZE:
cxBlock = LOWORD(lParam) / DIVISIONS;
cyBlock = HIWORD(lParam) / DIVISIONS;
return 0;
case WM_LBUTTONDOWN:
x = LOWORD(lParam) / cxBlock;
y = HIWORD(lParam) / cyBlock;
if (x < DIVISIONS && y < DIVISIONS)
{
fState[x][y] ^= 1;
rect.left = x * cxBlock;
rect.top = y * cyBlock;
rect.right = (x+1) * cxBlock;
rect.bottom = (y+1) * cyBlock;
InvalidateRect(hwnd, &rect, FALSE);
}
else
{
MessageBeep(0);
}
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
for (x = 0; x < DIVISIONS; x++)
{
for (y = 0; y < DIVISIONS; y++)
{
Rectangle(hdc, x*cxBlock, y*cyBlock, (x+1)*cxBlock, (y+1)*cyBlock);
if (fState[x][y])
{
MoveToEx(hdc, x*cxBlock, y*cyBlock, NULL);
LineTo (hdc, (x+1)*cxBlock, (y+1)*cyBlock);
MoveToEx(hdc, x*cxBlock, (y+1)*cyBlock, NULL);
LineTo (hdc, (x+1)*cxBlock, y*cyBlock);
}
}
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
7.使用鍵盤模仿滑鼠:
為上面的程式新增鍵盤介面:
case WM_SETFOCUS:
ShowCursor(TRUE);
return 0;
case WM_KILLFOCUS:
ShowCursor(FALSE);
return 0;
case WM_KEYDOWN:
GetCursorPos(&point);
ScreenToClient(hwnd, &point);
x = max(0, min(DIVISIONS-1, point.x/cxBlock));
y = max(0, min(DIVISIONS-1, point.y/cyBlock));
switch (wParam)
{
case VK_UP:
y--;
break;
case VK_DOWN:
y++;
break;
case VK_LEFT:
x--;
break;
case VK_RIGHT:
x++;
break;
case VK_HOME:
x = y = 0;
break;
case VK_END:
x = y = DIVISIONS-1;
break;
case VK_SPACE:
case VK_RETURN:
SendMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(x * cxBlock, y * cyBlock));
break;
}
x = (x + DIVISIONS) % DIVISIONS;
y = (y + DIVISIONS) % DIVISIONS;
point.x = x*cxBlock + cxBlock/2;
point.y = y*cyBlock + cyBlock/2;
ClientToScreen(hwnd, &point);
SetCursorPos(point.x, point.y);
return 0;
8.使用子視窗
建立25個子視窗,用於處理滑鼠單擊操作:
/*****************************************************************************
checker.cpp -- Mouse Hit-Test Demo Program ver3
*****************************************************************************/
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildWindProc(HWND, UINT, WPARAM, LPARAM);
TCHAR szChildClass[] = TEXT("Checker3_child");
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Checker3");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
wndclass.lpfnWndProc = ChildWindProc;
wndclass.cbWndExtra = sizeof(long);
wndclass.hIcon = NULL;
wndclass.lpszClassName = szChildClass;
RegisterClass(&wndclass);
hwnd = CreateWindow(szAppName, // window class name
TEXT("Checker2 Mouse Hit-Test Demo"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hwndChild[DIVISIONS][DIVISIONS];
int cxBlock, cyBlock, x, y;
switch (message)
{
case WM_CREATE:
for (x = 0; x < DIVISIONS; x++)
for (y = 0; y < DIVISIONS; y++)
hwndChild[x][y] = CreateWindow(szChildClass, NULL, WS_CHILDWINDOW | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU)(y << 8 | x),
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
return 0;
case WM_SIZE:
cxBlock = LOWORD(lParam) / DIVISIONS;
cyBlock = HIWORD(lParam) / DIVISIONS;
for (x = 0; x < DIVISIONS; x++)
for (y = 0; y < DIVISIONS; y++)
MoveWindow(hwndChild[x][y], x*cxBlock, y*cyBlock, cxBlock, cyBlock, TRUE);
return 0;
case WM_LBUTTONDOWN:
MessageBeep(0);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK ChildWindProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
SetWindowLong(hwnd, 0, 0);
return 0;
case WM_LBUTTONDOWN:
SetWindowLong(hwnd, 0, 1 ^ GetWindowLong(hwnd, 0));
InvalidateRect(hwnd, NULL, FALSE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
Rectangle(hdc, 0, 0, rect.right, rect.bottom);
if (GetWindowLong(hwnd, 0))
{
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, rect.right, rect.bottom);
MoveToEx(hdc, 0, rect.bottom, NULL);
LineTo(hdc, rect.right, 0);
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
新增鍵盤控制:
為每個子視窗定義一個唯一的“子視窗ID”,ID值由矩形的x和y座標組合而成。為了獲取一個具體子視窗ID,可用
idChild = GetWindowLong(hwndChild, GWL_ID);
idChild = GetDlgCtrlID(hwndCHild);
由父視窗控制代碼和子視窗ID得到子視窗控制代碼:
hwndChild = GetDlgItem(hwndParent, idChild);
程式碼如下:
/*****************************************************************************
checker.cpp -- Mouse Hit-Test Demo Program ver3
*****************************************************************************/
#include <windows.h>
#define DIVISIONS 5
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ChildWindProc(HWND, UINT, WPARAM, LPARAM);
TCHAR szChildClass[] = TEXT("Checker3_child");
int idFocus = 0;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Checker3");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
wndclass.lpfnWndProc = ChildWindProc;
wndclass.cbWndExtra = sizeof(long);
wndclass.hIcon = NULL;
wndclass.lpszClassName = szChildClass;
RegisterClass(&wndclass);
hwnd = CreateWindow(szAppName, // window class name
TEXT("Checker2 Mouse Hit-Test Demo"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static HWND hwndChild[DIVISIONS][DIVISIONS];
int cxBlock, cyBlock, x, y;
switch (message)
{
case WM_CREATE:
for (x = 0; x < DIVISIONS; x++)
for (y = 0; y < DIVISIONS; y++)
hwndChild[x][y] = CreateWindow(szChildClass, NULL, WS_CHILDWINDOW | WS_VISIBLE,
0, 0, 0, 0,
hwnd, (HMENU)(y << 8 | x),
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL);
return 0;
case WM_SIZE:
cxBlock = LOWORD(lParam) / DIVISIONS;
cyBlock = HIWORD(lParam) / DIVISIONS;
for (x = 0; x < DIVISIONS; x++)
for (y = 0; y < DIVISIONS; y++)
MoveWindow(hwndChild[x][y], x*cxBlock, y*cyBlock, cxBlock, cyBlock, TRUE);
return 0;
case WM_LBUTTONDOWN:
MessageBeep(0);
return 0;
case WM_SETFOCUS:
MessageBeep(0);
return 0;
case WM_KEYDOWN:
x = idFocus & 0xFF;
y = idFocus >> 8;
switch (wParam)
{
case VK_UP:
y--;
break;
case VK_DOWN:
y++;
break;
case VK_LEFT:
x--;
break;
case VK_RIGHT:
x++;
break;
case VK_HOME:
x = y = 0;
break;
case VK_END:
x = y = DIVISIONS-1;
break;
default:
return 0;
}
x = (x + DIVISIONS) % DIVISIONS;
y = (y + DIVISIONS) % DIVISIONS;
idFocus = y << 8 | x;
SetFocus(GetDlgItem(hwnd, idFocus));
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK ChildWindProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
SetWindowLong(hwnd, 0, 0);
return 0;
case WM_KEYDOWN:
if (wParam != VK_RETURN && wParam != VK_SPACE)
{
SendMessage(GetParent(hwnd), message, wParam, lParam);
return 0;
}
case WM_LBUTTONDOWN:
SetWindowLong(hwnd, 0, 1 ^ GetWindowLong(hwnd, 0));
SetFocus(hwnd);
InvalidateRect(hwnd, NULL, FALSE);
return 0;
case WM_SETFOCUS:
idFocus = GetWindowLong(hwnd, GWL_ID);
case WM_KILLFOCUS:
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
Rectangle(hdc, 0, 0, rect.right, rect.bottom);
if (GetWindowLong(hwnd, 0))
{
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, rect.right, rect.bottom);
MoveToEx(hdc, 0, rect.bottom, NULL);
LineTo(hdc, rect.right, 0);
}
if (hwnd == GetFocus())
{
rect.left += rect.right / 10;
rect.right -= rect.left;
rect.top += rect.bottom / 10;
rect.bottom -= rect.top;
SelectObject(hdc, GetStockObject(NULL_BRUSH));
SelectObject(hdc, CreatePen(PS_DASH, 0, 0));
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
9.捕獲滑鼠
當滑鼠處於視窗範圍之外時,“捕獲”滑鼠。
呼叫SetCapture(hwnd); Windows會將所有滑鼠訊息傳送給視窗控制代碼為hwnd的視窗的視窗過程。滑鼠訊息總是以客戶區訊息的形式出現,即使滑鼠位於視窗的非客戶區。
呼叫ReleaseCapture(); 釋放滑鼠
/*****************************************************************************
blockout.cpp -- Mouse Button Demo Program
*****************************************************************************/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("BlokOut1");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, // window class name
TEXT("Checker2 Mouse Hit-Test Demo"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
void DrawBoxOutline(HWND hwnd, POINT ptBeg, POINT ptEnd)
{
HDC hdc;
hdc = GetDC(hwnd);
SetROP2(hdc, R2_NOT);
SelectObject(hdc, GetStockObject(NULL_BRUSH));
Rectangle(hdc, ptBeg.x, ptBeg.y, ptEnd.x, ptEnd.y);
ReleaseDC(hwnd, hdc);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static BOOL fBlocking, fValidBox;
static POINT ptBeg, ptEnd, ptBoxBeg, ptBoxEnd;
HDC hdc;
PAINTSTRUCT ps;
switch (message)
{
case WM_LBUTTONDOWN:
ptBeg.x = ptEnd.x = LOWORD(lParam);
ptBeg.y = ptEnd.y = HIWORD(lParam);
DrawBoxOutline(hwnd, ptBeg, ptEnd);
SetCapture(hwnd);
SetCursor(LoadCursor(NULL, IDC_CROSS));
fBlocking = TRUE;
return 0;
case WM_MOUSEMOVE:
if (fBlocking)
{
SetCursor(LoadCursor(NULL, IDC_CROSS));
DrawBoxOutline(hwnd, ptBeg, ptEnd);
ptEnd.x = LOWORD(lParam);
ptEnd.y = HIWORD(lParam);
DrawBoxOutline(hwnd, ptBeg, ptEnd);
}
return 0;
case WM_LBUTTONUP:
if (fBlocking)
{
DrawBoxOutline(hwnd, ptBeg, ptEnd);
ptBoxBeg = ptBeg;
ptBoxEnd.x = LOWORD(lParam);
ptBoxEnd.y = HIWORD(lParam);
ReleaseCapture();
SetCursor(LoadCursor(NULL, IDC_ARROW));
fBlocking = FALSE;
fValidBox = TRUE;
InvalidateRect(hwnd, NULL, TRUE);
}
return 0;
case WM_CHAR:
if (fBlocking & (wParam == '\x1B'))
{
DrawBoxOutline(hwnd, ptBeg, ptEnd);
ReleaseCapture();
SetCursor(LoadCursor(NULL, IDC_ARROW));
fBlocking = FALSE;
}
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
if (fValidBox)
{
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
Rectangle(hdc, ptBoxBeg.x, ptBoxBeg.y, ptBoxEnd.x, ptBoxEnd.y);
}
if (fBlocking)
{
SetROP2(hdc, R2_NOT);
SelectObject(hdc, GetStockObject(NULL_BRUSH));
Rectangle(hdc, ptBeg.x, ptBeg.y, ptBoxEnd.x, ptBoxEnd.y);
}
EndPaint(hwnd,&ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK ChildWindProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
SetWindowLong(hwnd, 0, 0);
return 0;
case WM_KEYDOWN:
if (wParam != VK_RETURN && wParam != VK_SPACE)
{
SendMessage(GetParent(hwnd), message, wParam, lParam);
return 0;
}
case WM_LBUTTONDOWN:
SetWindowLong(hwnd, 0, 1 ^ GetWindowLong(hwnd, 0));
SetFocus(hwnd);
InvalidateRect(hwnd, NULL, FALSE);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
Rectangle(hdc, 0, 0, rect.right, rect.bottom);
if (GetWindowLong(hwnd, 0))
{
MoveToEx(hdc, 0, 0, NULL);
LineTo(hdc, rect.right, rect.bottom);
MoveToEx(hdc, 0, rect.bottom, NULL);
LineTo(hdc, rect.right, 0);
}
if (hwnd == GetFocus())
{
rect.left += rect.right / 10;
rect.right -= rect.left;
rect.top += rect.bottom / 10;
rect.bottom -= rect.top;
SelectObject(hdc, GetStockObject(NULL_BRUSH));
SelectObject(hdc, CreatePen(PS_DASH, 0, 0));
Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
10.滑鼠滾輪
滾輪的滾動使Windows產生WM_MOUSEWHEEL訊息,併發送給具有輸入焦點的視窗。lParam包含滑鼠的位置資訊,這些座標是相對於螢幕左上角的座標,而不是相對客戶區的座標。
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// cxChar平均字元寬度,cyChar字元的總高度(包括外部間距),cxCaps大寫字元的平均寬度
// 等寬字型中,cxCaps等於cxChar,變寬字型中,cxCaps等於cxChar的1.5倍
static int cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth, iVscrollPos;
static int iDeltaPerLine, iAccumDelta;
HDC hdc;
int i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd;
PAINTSTRUCT ps;
SCROLLINFO si;
TCHAR szBuffer[10];
TEXTMETRIC tm;
ULONG ulScrollLines;
switch (message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm); // 獲取系統預設字型的尺寸
cxChar = tm.tmAveCharWidth;
// tmPitchAndFamily為1表示變寬字型,為0表示等寬字型
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hwnd, hdc);
iMaxWidth = 40*cxChar + 22*cxCaps;
case WM_SETTINGCHANGE:
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ulScrollLines, 0);
if (ulScrollLines)
iDeltaPerLine = WHEEL_DELTA / ulScrollLines;
else
iDeltaPerLine = 0;
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = NUMLINES-1;
si.nPage = cyClient / cyChar;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = 2 + iMaxWidth/cxChar;
si.nPage = cxClient / cxChar;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
return 0;
case WM_VSCROLL:
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_VERT, &si);
iVertPos = si.nPos;
switch (LOWORD(wParam))
{
case SB_TOP:
si.nPos = si.nMin;
break;
case SB_BOTTOM:
si.nPos = si.nMax;
break;
case SB_LINEUP:
si.nPos -= 1;
break;
case SB_LINEDOWN:
si.nPos += 1;
break;
case SB_PAGEUP:
si.nPos -= si.nPage;
break;
case SB_PAGEDOWN:
si.nPos += si.nPage;
break;
case SB_THUMBTRACK:
si.nPos = si.nTrackPos;
break;
default:
break;
}
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
GetScrollInfo(hwnd, SB_VERT, &si);
if (si.nPos != iVertPos)
{
ScrollWindow(hwnd, 0, cyChar*(iVertPos-si.nPos), NULL, NULL);
UpdateWindow(hwnd);
}
return 0;
case WM_HSCROLL:
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
GetScrollInfo(hwnd, SB_HORZ, &si);
iHorzPos = si.nPos;
switch (LOWORD(wParam))
{
case SB_LINELEFT:
si.nPos -= 1;
break;
case SB_LINERIGHT:
si.nPos += 1;
break;
case SB_PAGELEFT:
si.nPos -= si.nPage;
break;
case SB_PAGERIGHT:
si.nPos += si.nPage;
break;
case SB_THUMBPOSITION:
si.nPos = si.nTrackPos;
break;
default:
break;
}
si.fMask = SIF_POS;
SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
GetScrollInfo(hwnd, SB_HORZ, &si);
if (si.nPos != iHorzPos)
{
ScrollWindow(hwnd, cxChar*(iHorzPos-si.nPos), 0, NULL, NULL);
UpdateWindow(hwnd);
}
return 0;
case WM_KEYDOWN:
switch (wParam)
{
case VK_HOME:
SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);
break;
case VK_END:
SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0);
break;
case VK_PRIOR:
SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
break;
case VK_NEXT:
SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
break;
case VK_UP:
SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
break;
case VK_DOWN:
SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
break;
case VK_LEFT:
SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP, 0);
break;
case VK_RIGHT:
SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0);
break;
}
return 0;
case WM_MOUSEWHEEL:
if (iDeltaPerLine == 0)
break;
iAccumDelta += (short)HIWORD(wParam);
while (iAccumDelta >= iDeltaPerLine)
{
SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
iAccumDelta -= iDeltaPerLine;
}
while (iAccumDelta <= -iDeltaPerLine)
{
SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
iAccumDelta += iDeltaPerLine;
}
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
GetScrollInfo(hwnd, SB_VERT, &si);
iVertPos = si.nPos;
GetScrollInfo(hwnd, SB_HORZ, &si);
iHorzPos = si.nPos;
iPaintBeg = max(0, iVertPos + ps.rcPaint.top/cyChar);
iPaintEnd = min(NUMLINES-1, iVertPos + ps.rcPaint.bottom/cyChar);
for (i = iPaintBeg; i <= iPaintEnd; i++)
{
x = cxChar * (1-iHorzPos);
y = cyChar * (i-iVertPos);
TextOut(hdc, x, y, sysmetrics[i].szLabel, lstrlen(sysmetrics[i].szLabel));
TextOut(hdc, x + 22*cxCaps, y, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc));
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
TextOut(hdc, x + 22*cxCaps + 40*cxChar, y, szBuffer, wsprintf(szBuffer, TEXT("%5d"),
GetSystemMetrics(sysmetrics[i].iIndex)));
// 將對齊方式設回正常方式
SetTextAlign(hdc, TA_LEFT | TA_TOP);
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
可以用滾輪了~