Win32 鼠标消息 - 客户区鼠标消息、非客户区鼠标消息、击中测试、鼠标滚轮

注:以下内容为学习笔记,多数是从书本、资料中得来,只为加深印象,及日后参考。然而本人表达能力较差,写的不好。因非翻译、非转载,只好选原创,但多数乃摘抄,实为惭愧。但若能帮助一二访客,幸甚!


注:以下内容多数摘自《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);
}

可以用滚轮了~

  • 7
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值