
本文共 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")};
#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 可我不想用鼠标
第六章将响应键盘的操作
发表评论
最新留言
关于作者
