《Windows程序设计》读书笔四 文本输出
发布日期:2021-05-07 23:35:02 浏览次数:20 分类:原创文章

本文共 24067 字,大约阅读时间需要 80 分钟。

客户区是指整个应用程序窗口中没有被标题栏,边框,菜单栏,工具栏,状态栏和滚动条占用的区域。就是窗口中程序可以在上面绘制并向用户传达可视化信息的区域


4.1 绘制和重绘

windows是一个消息驱动的系统,他使用两种方式把事件通知给应用程序。1)把消息放在应用程序消息队列中2)或者适当向窗口过程发送消息。

4.1.1 WM_PAINT消息

在进入消息循环之前调用UpdateWindow函数,向窗口过程发送最初的WM_PAINT消息,通知窗口过程绘制客户区。

以下事件都可能导致窗口收到WM_PAINT消息:

* 用户移动一个窗口,导致原来被遮盖的部分窗口暴露出来。

* 用户调整窗口的大小(当窗口类型设定为CS_HREDRAW 和 CS_VREDRAW)

* 程序调用ScrollWindow或ScrollDC函数滚动客户区

* InvalidateRect 或InvalidateRgn函数显示生成WM_PAINT消息


某些情况下windows会试图保持被覆盖的客户区一遍恢复时使用,但并非每次都成功。以下情形windows有时会发送一条WM_PAINT消息

* Windows 关闭一个覆盖了部分窗口的对话框或消息

* 下拉菜单被拉下然后收回

* 显示提示信息

少数情况下windows总会保存被覆盖区的显示内容

* 鼠标指针在客户区内移动

* 在客户区内拖动图标


4.1.2 有效矩形和无效矩形

需要重新绘制的区域称为“无效区域”,导致windows在消息队列中放置一条WM_PAINT消息,并更新一个“绘制信息结构”,保持着无效区域的矩形。当收到WM_PAINT消息的时候可以使用GetUpdateRect来获得无效矩形的坐标。

在BeginPaint函数以后,整个客户区会变成有效。也可以调用ValidateRect是客户区中任意矩形变得有效,如果该函数让整个无效区都变得有效,那么当前消息队列中的WM_PAINT消息就会被删除。


4.2 GDI简介

最常用的字符输出函数是TextOut

TextOut(hdc, x, y, psText, iLength);

4.2.1 设备环境Device context

设备环境是GDI内部维护的一种数据结构,与特定的显示设备相关联(显示器或打印机)

设备环境中的某些值是图形“属性”,这些属性决定了GDI绘图函数的工作细节。例如在TextOut函数中,设备环境的属性决定着文本颜色,背景颜色。

GDI函数可以改变设备环境的默认属性,也可以获得当前值,还有一些GDI函数可以让你在窗口区绘图。

在绘图完成以后,必须释放设备环境

4.2.2 获取设备环境的方法:方法一

在WM_PAINT消息时 BeginPaint 获取

4.2.3 绘制信息结构

调用BeginPaint函数windows自动填充该结构

typedef struct tagPAINTSTRUCT {    HDC         hdc;    BOOL        fErase;    RECT        rcPaint;    BOOL        fRestore;    BOOL        fIncUpdate;    BYTE        rgbReserved[32];} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;

fErase 设为false, 表明在先前BeginPaint函数已经擦除了无效区域。

使用InvalidateRect是客户区矩形无效时,最后一个参数指定背景是否要擦除,fasle windows不会擦除背景

InvalidateRect(hwnd, NULL, TRUE); 是整个客户区无效化,其后调用BeginPaint会擦除原有背景。 设为false会保留原有背景

4.2.4 获取设备环境句柄:方法二

有些时候在非WM_PAINT消息也需要绘制客户区。

hdc = GetDC(hwnd);

Release(hwnd, hdc);  两者成对出现,不要夸消息。

GetDC获取的是整个客户区,不会使无效矩形有效化。如果要是整个客户区有效化,使用

ValidateRect(hwnd, NULL) 函数

通常用户处理键盘鼠标消息,而不用使用BeginPaint使客户区无效化。

GetWindowDC 获取窗口的设备环境句柄。也要处理非客户区绘制  WM_NCPAINT


4.2.5 TextOut函数详解

TextOut(hdc, x, y, psText, iLength);

hdc 设备环境,通过BeginPaint 或者GetDC获得, 默认有一系列字体属性

psText 显示文本的指针,

iLength 文本长度

x,y 逻辑坐标

windows将不会显示字符串落在剪裁区域以外的部分。


4.2.6 系统字体

设备环境定义了在调用TextOut时候的Windows使用的字体,默认系统字体(SYSTEM_FONT)

4.2.7 字符大小

GetSystemMetrics 获取用户界面的尺寸,

GetTextMetrics 获取字体尺寸,返回当前设备环境中选中字体的信息。

typedef struct tagTEXTMETRICW{    LONG        tmHeight;    LONG        tmAscent;    LONG        tmDescent;    LONG        tmInternalLeading;    LONG        tmExternalLeading;    LONG        tmAveCharWidth;    LONG        tmMaxCharWidth;    LONG        tmWeight;    LONG        tmOverhang;    LONG        tmDigitizedAspectX;    LONG        tmDigitizedAspectY;    WCHAR       tmFirstChar;    WCHAR       tmLastChar;    WCHAR       tmDefaultChar;    WCHAR       tmBreakChar;    BYTE        tmItalic;    BYTE        tmUnderlined;    BYTE        tmStruckOut;    BYTE        tmPitchAndFamily;    BYTE        tmCharSet;} TEXTMETRICW, *PTEXTMETRICW, NEAR *NPTEXTMETRICW, FAR *LPTEXTMETRICW;
默认映射模式是MM_TEXT 所以以像素为单位

获取字符相关信息

TEXTMETRIC tm

hdc = GetDC(hwnd);

GetTextMetrics(hdc, &tm);

ReleaseDC(hwnd, hdc);


4.2.8 文本尺寸的度量

TEXTMETRIC 结构中有设备环境中当前字体的各种信息。

tmHeight  是tmAscent(基线之上)和tmDescent(基线之下)之和

tmInternalLeading(内部间距)


tmExternalLeading 字体设计者建议在两行文字间流出的空间大小。

tmAveCharWidth 小写字符的加权平均宽度,

tmMaxCharWidth 字体中最宽的字符的宽度

不要猜测字体尺寸,不用使用固定值,应该通过GetTextMetrics函数来获得信息。


4.2.9 文本格式化

在WM_CREATE消息时调用一次GetTextMetrics函数

case WM_CREATE:    hdc = getDC(hwnd);    GetTextMetrics(hdc, &tm);    cxChar = tm.tmAveCharWidth;    cyChar = tm.tmHeight + tm.tmExternalLeading;    ReleaseDC(hwnd, hdc);    return 0;

一个wsprintf 和TextOut组合的例子

int iLength;TCHAR szBuffer[40];iLength = wsprintf(szBuffer, TEXT("The sum of %i and %i is %i"), iA, iB, iA + iB);TextOut(HDC, x, y, szBuffer, iLength);

也可以合并成一句

TextOut(hdc, x, y, szBuffer,	wsprintf(szBuffer, TEXT("The sum of %i and %i is %i"), iA, iB, iA + iB));

4.2.10 综合使用

GetSystemMetrics 获得windows中各种图形项(图标,鼠标指针,标题栏和滚动条)的尺寸信息。也称为“索引”参数。

一个使用GetSystemMetrics函数获取系统信息的例子

sysmets.h

#define NUMLINES ((int)(sizeof sysmetrics / sizeof sysmetrics[0]))struct {	int		iIndex;	TCHAR*	szLabel;	TCHAR*	szDesc;}sysmetrics[] ={	SM_CXSCREEN, TEXT("SM_CXSCREEN"),	TEXT("Screen width in pixels"),	SM_CYSCREEN, TEXT("SM_CYSCREEN"),	TEXT("Screen height in pixels"),	SM_CXVSCROLL, TEXT("SM_CXVSCROLL"),	TEXT("Vertical scroll width"),	SM_CYHSCROLL, TEXT("SM_CYHSCROLL"),	TEXT("Horizontal scroll height"),	SM_CYCAPTION, TEXT("SM_CYCAPTION"),	TEXT("Caption bar height"),	SM_CXBORDER, TEXT("SM_CXBORDER"),	TEXT("Window border width"),	SM_CYBORDER, TEXT("SM_CYBORDER"),	TEXT("Window border height"),	SM_CXFIXEDFRAME, TEXT("SM_CXFIXEDFRAME"),	TEXT("Dialog window frame width"),	SM_CYFIXEDFRAME, TEXT("SM_CYFIXEDFRAME"),	TEXT("Dialog window frame height"),	SM_CYVTHUMB, TEXT("SM_CYVTHUMB"),	TEXT("Vertical scroll thumb height"),	SM_CXHTHUMB, TEXT("SM_CXHTHUMB"),	TEXT("Horizontal scroll thumb width"),	SM_CXICON, TEXT("SM_CXICON"),	TEXT("Icon width"),	SM_CYICON, TEXT("SM_CYICON"),	TEXT("Icon height"),	SM_CXCURSOR, TEXT("SM_CXCURSOR"),	TEXT("Cursor width"),	SM_CYCURSOR, TEXT("SM_CYCURSOR"),	TEXT("Cursor height"),	SM_CYMENU, TEXT("SM_CYMENU"),	TEXT("Menu bar height"),	SM_CXFULLSCREEN, TEXT("SM_CXFULLSCREEN"),	TEXT("Full screen client area width"),	SM_CYFULLSCREEN, TEXT("SM_CYFULLSCREEN"),	TEXT("Full screen client area height"),	SM_CYKANJIWINDOW, TEXT("SM_CYKANJIWINDOW"),	TEXT("Kanji window height"),	SM_MOUSEPRESENT, TEXT("SM_MOUSEPRESENT"),	TEXT("Mouse present flag"),	SM_CYVSCROLL, TEXT("SM_CYVSCROLL"),	TEXT("Vertical scroll arrow height"),	SM_CXHSCROLL, TEXT("SM_CXHSCROLL"),	TEXT("Horizontal scroll arrow width"),	SM_DEBUG, TEXT("SM_DEBUG"),	TEXT("Debug version flag"),	SM_SWAPBUTTON, TEXT("SM_SWAPBUTTON"),	TEXT("Mouse buttons swapped flag"),	SM_CXMIN, TEXT("SM_CXMIN"),	TEXT("Minimum window width"),	SM_CYMIN, TEXT("SM_CYMIN"),	TEXT("Minimum window height"),	SM_CXSIZE, TEXT("SM_CXSIZE"),	TEXT("Min/Max/Close button width"),	SM_CYSIZE, TEXT("SM_CYSIZE"),	TEXT("Min/Max/Close button height"),	SM_CXSIZEFRAME, TEXT("SM_CXSIZEFRAME"),	TEXT("Window sizing frame width"),	SM_CYSIZEFRAME, TEXT("SM_CYSIZEFRAME"),	TEXT("Window sizing frame height"),	SM_CXMINTRACK, TEXT("SM_CXMINTRACK"),	TEXT("Minimum window tracking width"),	SM_CYMINTRACK, TEXT("SM_CYMINTRACK"),	TEXT("Minimum window tracking height"),	SM_CXDOUBLECLK, TEXT("SM_CXDOUBLECLK"),	TEXT("Double click x tolerance"),	SM_CYDOUBLECLK, TEXT("SM_CYDOUBLECLK"),	TEXT("Double click y tolerance"),	SM_CXICONSPACING, TEXT("SM_CXICONSPACING"),	TEXT("Horizontal icon spacing"),	SM_CYICONSPACING, TEXT("SM_CYICONSPACING"),	TEXT("Vertical icon spacing"),	SM_MENUDROPALIGNMENT, TEXT("SM_MENUDROPALIGNMENT"),	TEXT("Left or right menu drop"),	SM_PENWINDOWS, TEXT("SM_PENWINDOWS"),	TEXT("Pen extensions installed"),	SM_DBCSENABLED, TEXT("SM_DBCSENABLED"),	TEXT("Double-Byte Char Set enabled"),	SM_CMOUSEBUTTONS, TEXT("SM_CMOUSEBUTTONS"),	TEXT("Number of mouse buttons"),	SM_SECURE, TEXT("SM_SECURE"),	TEXT("Security present flag"),	SM_CXEDGE, TEXT("SM_CXEDGE"),	TEXT("3-D border width"),	SM_CYEDGE, TEXT("SM_CYEDGE"),	TEXT("3-D border height"),	SM_CXMINSPACING, TEXT("SM_CXMINSPACING"),	TEXT("Minimized window spacing width"),	SM_CYMINSPACING, TEXT("SM_CYMINSPACING"),	TEXT("Minimized window spacing height"),	SM_CXSMICON, TEXT("SM_CXSMICON"),	TEXT("Small icon width"),	SM_CYSMICON, TEXT("SM_CYSMICON"),	TEXT("Small icon height"),	SM_CYSMCAPTION, TEXT("SM_CYSMCAPTION"),	TEXT("Small caption height"),	SM_CXSMSIZE, TEXT("SM_CXSMSIZE"),	TEXT("Small caption button width"),	SM_CYSMSIZE, TEXT("SM_CYSMSIZE"),	TEXT("Small caption button height"),	SM_CXMENUSIZE, TEXT("SM_CXMENUSIZE"),	TEXT("Menu bar button width"),	SM_CYMENUSIZE, TEXT("SM_CYMENUSIZE"),	TEXT("Menu bar button height"),	SM_ARRANGE, TEXT("SM_ARRANGE"),	TEXT("How minimized windows arranged"),	SM_CXMINIMIZED, TEXT("SM_CXMINIMIZED"),	TEXT("Minimized window width"),	SM_CYMINIMIZED, TEXT("SM_CYMINIMIZED"),	TEXT("Minimized window height"),	SM_CXMAXTRACK, TEXT("SM_CXMAXTRACK"),	TEXT("Maximum draggable width"),	SM_CYMAXTRACK, TEXT("SM_CYMAXTRACK"),	TEXT("Maximum draggable height"),	SM_CXMAXIMIZED, TEXT("SM_CXMAXIMIZED"),	TEXT("Width of maximized window"),	SM_CYMAXIMIZED, TEXT("SM_CYMAXIMIZED"),	TEXT("Height of maximized window"),	SM_NETWORK, TEXT("SM_NETWORK"),	TEXT("Network present flag"),	SM_CLEANBOOT, TEXT("SM_CLEANBOOT"),	TEXT("How system was booted"),	SM_CXDRAG, TEXT("SM_CXDRAG"),	TEXT("Avoid drag x tolerance"),	SM_CYDRAG, TEXT("SM_CYDRAG"),	TEXT("Avoid drag y tolerance"),	SM_SHOWSOUNDS, TEXT("SM_SHOWSOUNDS"),	TEXT("Present sounds visually"),	SM_CXMENUCHECK, TEXT("SM_CXMENUCHECK"),	TEXT("Menu check-mark width"),	SM_CYMENUCHECK, TEXT("SM_CYMENUCHECK"),	TEXT("Menu check-mark height"),	SM_SLOWMACHINE, TEXT("SM_SLOWMACHINE"),	TEXT("Slow processor flag"),	SM_MIDEASTENABLED, TEXT("SM_MIDEASTENABLED"),	TEXT("Hebrew and Arabic enabled flag"),	SM_MOUSEWHEELPRESENT, TEXT("SM_MOUSEWHEELPRESENT"),	TEXT("Mouse wheel present flag"),	SM_XVIRTUALSCREEN, TEXT("SM_XVIRTUALSCREEN"),	TEXT("Virtual screen x origin"),	SM_YVIRTUALSCREEN, TEXT("SM_YVIRTUALSCREEN"),	TEXT("Virtual screen y origin"),	SM_CXVIRTUALSCREEN, TEXT("SM_CXVIRTUALSCREEN"),	TEXT("Virtual screen width"),	SM_CYVIRTUALSCREEN, TEXT("SM_CYVIRTUALSCREEN"),	TEXT("Virtual screen height"),	SM_CMONITORS, TEXT("SM_CMONITORS"),	TEXT("Number of monitors"),	SM_SAMEDISPLAYFORMAT, TEXT("SM_SAMEDISPLAYFORMAT"),	TEXT("Same color format flag")};


sysmets1.cpp

#include <windows.h>#include "sysmets.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,	PSTR szCmdLine, int iCmdShow){	static		TCHAR szAppName[] = TEXT("SysMets1");	HWND		hwnd;	MSG			msg;	WNDCLASS	wndClass;		//The window Class	wndClass.style = CS_HREDRAW | CS_VREDRAW;	wndClass.lpfnWndProc	= WndProc;// assign the window procedure to windows class.	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;	//Register the Window Class to the Windows System. 	if (!RegisterClass(&wndClass))	{		MessageBox(NULL, TEXT("This program require Windows NT!"),			szAppName, MB_ICONERROR);		return 0;	}	//This function will generate an WM_CREATE message.	hwnd = CreateWindow(szAppName,		//Window class name		TEXT("Get System Metrics No. 1"),		//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);	//This function will generate a WM_PAINT message.	/* The message loop for this program.	if received the WM_QUIT message, the function will return 0.*/	while (GetMessage(&msg, NULL, 0, 0))	{		TranslateMessage(&msg);		DispatchMessage(&msg);	}	return msg.wParam;}//define the Window Procedure WndProcLRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){	static int	cxChar, cxCaps, cyChar;	HDC			hdc;	int			i;	PAINTSTRUCT	ps;	TCHAR		szBuffer[10];	TEXTMETRIC	tm;	switch (message) //get the message	{	case WM_CREATE:		hdc = GetDC(hwnd);		GetTextMetrics(hdc, &tm);		cxChar = tm.tmAveCharWidth;		cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2)* cxChar / 2;		cyChar = tm.tmHeight + tm.tmExternalLeading;		ReleaseDC(hwnd, hdc);		return 0;	case WM_PAINT:		hdc = BeginPaint(hwnd, &ps);		for (i = 0; i < NUMLINES; ++i)		{			TextOut(hdc, 0, cyChar * i,				sysmetrics[i].szLabel,				lstrlen(sysmetrics[i].szLabel));			TextOut(hdc, 22 * cxCaps, cyChar*i,				sysmetrics[i].szDesc,				lstrlen(sysmetrics[i].szDesc));			SetTextAlign(hdc, TA_RIGHT | TA_TOP);			TextOut(hdc, 22 * cxCaps + 40 * cxChar, cyChar * i, 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);}


执行结果如下



4.2.11  SYSMETS1.cpp 的窗口过程

WM_CREATE, WM_PAINT, WM_DESTROY

WM_CREATE消息, 使用GetTextMetrics获得系统字体的尺寸

cxChar 平均宽度,cyChar 字符总高度

cxCaps 大写字符的平均宽度,在等宽字体中cxCaps 等于cxChar, 在变宽字体中,cxCaps为cxChar的1.5倍。 tmPitchAndFamily字段低位决定是否为等宽字体:1代表变宽字体,0 代表等宽字体

cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;

WM_PAINT消息完成所有绘图工作。

cyChar * i  指定了每行字符的顶部相对客户区顶部的位置

第一个TextOut 输出大写标识符

第二个TextOut输出系统尺寸值的描述  22 * Caps 预留空位给第二列输出

第三个TextOut 输出从GetSystemMetrics函数获取的数值,并且右对齐。 边框字体的0~9 宽度相同。

SetTextAlign(hdc, TA_RIGHT|TA_TOP) 通知客户区从右上开始

4.2.12 空间不够

4.2.13 客户区尺寸

用SM_CXFULLSCREEN,  SM_CYFULLSCREEN 给GetSystemMetrics作为参数时,可以得到窗口最大化客户区的尺寸。

当窗口尺寸发生变化是Windows是发送WM_SIZE消息。

case WM_SIZE:

    cxClient = LOWORD(lParam);

    cyClient = HIWORD(lParam);

    return 0;

同城WM_SIZE 之后还会有WM_PAINT消息,因为我们定义了CS_HREDRAW | CS_VREDRAW

计算客户区可以显示的整行字符数:  cyClient / cyChar

可以水平显示的小写字符数  : cxClient / cxChar


4.3 滚动条

在CreateWindow的第三个参数包括窗口风格   WS_VSCROLL  或者  WS_HSCROLL

4.3.1 滚动条的范围和位置

SetScrollRange(hwnd, iBar, iMin, iMax, bRedraw);

iBar  SB_VERT, SB_HORZ

iMin, iMax 最小最大值

bRedraw 需要windows根据新的范围重绘滚动条bRedraw设为true,如果要调用其他函数来调整滚动条显示最好将bRedraw设为false


SetScrollPos(hwnd, iBar, iPos, bRedraw); 设定滑块在滚动条范围中的位置

GetScrollRange GetScrollPos


windows负责以下任务:

处理滚动条的所有鼠标消息

当用户单击滚动条,提供一种反向显示的闪烁

当用户拖动滑块,在滚动条内移动滑块

向右滚动条的窗口过程发送滚动条消息


程序负责以下任务:

初始化滚动条的范围和位置

处理传给窗口过程的滚动条消息

更新滑块的位置

根据滚动条的变化更新客户区的内容


4.3.2 滚动条消息

WM_VSCROLL,  WM_HSCROLL



4.3.3 加入滚动条的SYSMETS

#include <windows.h>#include "sysmets.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,	PSTR szCmdLine, int iCmdShow){	static		TCHAR szAppName[] = TEXT("SysMets1");	HWND		hwnd;	MSG			msg;	WNDCLASS	wndClass;		//The window Class	wndClass.style = CS_HREDRAW | CS_VREDRAW;	wndClass.lpfnWndProc	= WndProc;// assign the window procedure to windows class.	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;	//Register the Window Class to the Windows System. 	if (!RegisterClass(&wndClass))	{		MessageBox(NULL, TEXT("This program require Windows NT!"),			szAppName, MB_ICONERROR);		return 0;	}	//This function will generate an WM_CREATE message.	hwnd = CreateWindow(szAppName,		//Window class name		TEXT("Get System Metrics No. 1"),		//Window caption		WS_OVERLAPPEDWINDOW | WS_VSCROLL,			//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);	//This function will generate a WM_PAINT message.	/* The message loop for this program.	if received the WM_QUIT message, the function will return 0.*/	while (GetMessage(&msg, NULL, 0, 0))	{		TranslateMessage(&msg);		DispatchMessage(&msg);	}	return msg.wParam;}//define the Window Procedure WndProcLRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){	static int	cxChar, cxCaps, cyChar, cyClient, iVscrollPos;	HDC			hdc;	int			i, y;	PAINTSTRUCT	ps;	TCHAR		szBuffer[10];	TEXTMETRIC	tm;	switch (message) //get the message	{	case WM_CREATE:		hdc = GetDC(hwnd);		GetTextMetrics(hdc, &tm);		cxChar = tm.tmAveCharWidth;		cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2)* cxChar / 2;		cyChar = tm.tmHeight + tm.tmExternalLeading;		ReleaseDC(hwnd, hdc);		SetScrollRange(hwnd, SB_VERT, 0, NUMLINES - 1, FALSE);		SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);		return 0;	case WM_SIZE:		cyClient = HIWORD(lParam);		return 0;	case WM_VSCROLL:		switch (LOWORD(wParam))		{		case SB_LINEUP:			iVscrollPos -= 1;			break;		case SB_LINEDOWN:			iVscrollPos += 1;			break;		case SB_PAGEUP:			iVscrollPos -= cyClient / cyChar;			break;		case SB_PAGEDOWN:			iVscrollPos += cyClient / cyChar;		case SB_THUMBPOSITION:			iVscrollPos = HIWORD(wParam);			break;		default:			break;		}		iVscrollPos = max(0, min(iVscrollPos, NUMLINES - 1));		if (iVscrollPos != GetScrollPos(hwnd, SB_VERT))		{			SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);			InvalidateRect(hwnd, NULL, TRUE);		}		return 0;	case WM_PAINT:		hdc = BeginPaint(hwnd, &ps);		for (i = 0; i < NUMLINES; ++i)		{			y = cyChar*(i - iVscrollPos);			TextOut(hdc, 0, y,				sysmetrics[i].szLabel,				lstrlen(sysmetrics[i].szLabel));			TextOut(hdc, 22 * cxCaps, y,				sysmetrics[i].szDesc,				lstrlen(sysmetrics[i].szDesc));			SetTextAlign(hdc, TA_RIGHT | TA_TOP);			TextOut(hdc, 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);}

4.3.4 程序的绘制代码的结构

程序应该把所有客户区绘制工作限制在WM_PAINT消息时。因为程序必须能在收到WM_PAIN消息时绘制整个客户区

如果你想立刻更新无效区域,则可以在调用InvalidateRect函数后调用UpdateWindow函数

UpdateWindow(hwnd)


4.4 效果更好的滚动

4.4.1 滚动条信息函数

SetScrollInfo (hwnd, iBar, &si, bRedraw);

GetScrollInfo (hwnd, iBar, &si);

iBar   SB_VERT, SB_HORZ, SB_CTL

typedef struct tagSCROLLINFO{    UINT    cbSize;    UINT    fMask;    int     nMin;    int     nMax;    UINT    nPage;    int     nPos;    int     nTrackPos;}   SCROLLINFO, FAR *LPSCROLLINFO;

SCROLLINFO si;

si.cbSize = sizeof(si);  // ci.cbSize = sizeof(SCROLLINFO);

在SetScrollINfo中指定SIF_RANGE 必须在nMin 和nMax中指定滚动条范围。

SIF_POS   必须设定nPOS

SIF_PAGE nPage

SIF_TRACKPOS   只用作GetScrollInfo中,而且只能在通知吗是 SB_THUMBTRACK, SB_THUMBPOSITION的WM_VSCROLL 或者WM_HSCROLL的消息。

nTrackPos字段返回当前滑块的位置(该值为一个32位整数)

SIF_DISABLENOSCROLL  禁用滚动条

SIF_ALL, SIF_RANGE | SIF_POS | SIF_PAGE | SIF_TRACKPOS,    在SetScrollInfo中  SIF_TRACKPOS会被忽略

4.4.3 新的SYSMETS

#include <windows.h>#include "sysmets.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,	PSTR szCmdLine, int iCmdShow){	static		TCHAR szAppName[] = TEXT("SysMets1");	HWND		hwnd;	MSG			msg;	WNDCLASS	wndClass;		//The window Class	wndClass.style = CS_HREDRAW | CS_VREDRAW;	wndClass.lpfnWndProc	= WndProc;// assign the window procedure to windows class.	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;	//Register the Window Class to the Windows System. 	if (!RegisterClass(&wndClass))	{		MessageBox(NULL, TEXT("This program require Windows NT!"),			szAppName, MB_ICONERROR);		return 0;	}	//This function will generate an WM_CREATE message.	hwnd = CreateWindow(szAppName,		//Window class name		TEXT("Get System Metrics No. 1"),		//Window caption		WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,			//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);	//This function will generate a WM_PAINT message.	/* The message loop for this program.	if received the WM_QUIT message, the function will return 0.*/	while (GetMessage(&msg, NULL, 0, 0))	{		TranslateMessage(&msg);		DispatchMessage(&msg);	}	return msg.wParam;}//define the Window Procedure WndProcLRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){	static int	cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth;	HDC			hdc;	int			i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd;	PAINTSTRUCT	ps;	SCROLLINFO	si;	TCHAR		szBuffer[10];	TEXTMETRIC	tm;	switch (message) //get the message	{	case WM_CREATE:		hdc = GetDC(hwnd);		GetTextMetrics(hdc, &tm);		cxChar = tm.tmAveCharWidth;		cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2)* cxChar / 2;		cyChar = tm.tmHeight + tm.tmExternalLeading;		ReleaseDC(hwnd, hdc);		iMaxWidth = 40 * cxChar + 22 * cxCaps;		return 0;	case WM_SIZE:		cxClient = LOWORD(lParam);		cyClient = HIWORD(lParam);		//Set vertical scroll bar range and page size		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);		//Set the horizontal scroll bar range and page size		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:		//Get all the vertical scroll bar information		si.cbSize = sizeof(si);		si.fMask = SIF_ALL;		GetScrollInfo(hwnd, SB_VERT, &si);		//Save the position for comparison later on		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_THUMBPOSITION:			si.nPos = si.nTrackPos;			break;		default:			break;		}		//Set the position and then retrieve it. Due to adjustments		//By Windows it may not be the same as the value set.		si.fMask = SIF_POS;		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);		GetScrollInfo(hwnd, SB_VERT, &si);		// if the position has changed, scroll the window and update it		if (si.nPos != iVertPos)		{			ScrollWindow(hwnd, 0, cyChar*(iVertPos - si.nPos),				NULL, NULL);			UpdateWindow(hwnd); //instead of the invalidateRect() it will update the rect immediately.		}		return 0;	case WM_HSCROLL:		// Get all the vertical scroll bar information		si.cbSize = sizeof(si);		si.fMask = SIF_ALL;		//Save the position for comparison later on		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;		}		//Set the Position and then retrieve it. Due to adjustments		//by Windows it may not be the same as the value set.		si.fMask = SIF_POS;		SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);		GetScrollInfo(hwnd, SB_HORZ, &si);		//If the position has been changed, scroll the window		if (si.nPos != iHorzPos)		{			ScrollWindow(hwnd, cxChar* (iHorzPos - si.nPos), 0,				NULL, NULL);		}		return 0;	case WM_PAINT:		hdc = BeginPaint(hwnd, &ps);		//Get the vertical scroll bar position		si.cbSize = sizeof(si);;		si.fMask = SIF_POS;		GetScrollInfo(hwnd, SB_VERT, &si);		iVertPos = si.nPos;		//Get horizontal scroll bar position		GetScrollInfo(hwnd, SB_HORZ, &si);		iHorzPos = si.nPos;		//Find painting limits		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);}

执行结果



4.4.4 可我不想用鼠标

第六章将响应键盘的操作

上一篇:《Windows程序设计》读书笔五 绘图基础
下一篇:让程序员立于不败之地的七种武器

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2025年04月05日 17时10分28秒