在客户区中央输出单行文本:
DrawText(hdc,TEXT("Hello World ! "),-1,&rect,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
一,绘制和重绘
在 Windows 中,程序在窗口客户区显示的文本和图形,可能在重新输出时消失,例如,用户移动其他窗口时遮住你的程序的部分窗口,Windows 不会保留被遮住的部分窗口,移开后 Windows 会要求你的程序重绘刚刚被遮住的客户区(发送 WM_PAINT)。
1. WM_PAINT 消息
窗口过程会收到 WM_PAINT 消息的情况:
- 部分窗口被遮住后又暴露出来
- 用户调整窗口大小
- 程序调用 ScrollWindow 或 ScrollDC 函数滚动客户区
- 程序调用 InvalidateRect 或 InvalidateRgn 函数显式生成 WM_PAINT 消息。
有些情况,WIndows 可能保存被覆盖的部分:
- Windows 关闭一个覆盖了部分窗口的对话框或消息框。
- 下拉菜单被拉下然后收回
- 显示提示消息’
少数情况,Windows 总会保存被覆盖部分的显示内容,然后再恢复:
- 鼠标指针在客户区移动
- 在客户区内拖动图标
所以,我们的程序应该收集并保存所有用于绘制客户区的信息。
2.有效矩形和无效矩形
无效矩形产生WM_PAINT
需要重绘的部分即为“无效区域”或“更新区域”。客户区中有一个无效区域将导致 Windows 在程序的消息队列中放置一条 WM_PAINT 消息。
绘制信息结构
- Windows 内部为每个窗口保留“绘制信息结构”,该结构保存着一个可以覆盖该无效区域的最小矩形坐标和一些其他信息。
- 若在处理 WM_PAINT 消息之前,又产生了无效区域,Windows 将会计算出覆盖这两个失效部分的新的无效区域(无效矩形),并更新 绘制信息结构 中的数据。
=>Windows 不会在消息队列中放置多条 WM_PAINT 消息。
!其它! - 窗口过程可以通过 InvalidateRect 函数强制使客户区中的一个矩形失效
- 可以在处理 WM_PAINT 消息时获得无效矩形的坐标,
- 也可以在其他任何时候调用 GetUpdateRect 获取这些坐标。
- 处理 WM_PAINT 时,调用 BeginPaint 后整个客户区会变成有效的。
- 程序也可以调用 ValidateRect 函数使客户区中任意的矩形变得有效,若其让整个客户区都有效,那么消息队列中的 WM_PAINT 消息会被删除。
二,GDI 简介
1. 设备环境
设备环境句柄(HDC)(32位无符号整数)
- 是程序使用 GDI 函数的“通行证”
- 设备环境的属性决定绘制的一些特性,如颜色
- 当程序完成对客户区的绘制后,必须释放设备环境句柄(释放之后就无效了)
- 必须在处理同一条消息时,获取HDC并释放HDC,不能在两条消息中传递一个HDC(唯一例外是CreateDC 创建的设备环境)。
1.1 获取设备环境:方法一
处理 WM_PAINT 时使用。
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
(using GDI functions)
EndPaint(hwnd,&ps);
return 0;
BeginPaint() 函数:擦去无效区域,填充ps结构,返回设备环境句柄。
EndPaint() 函数:释放设备环境句柄。
在DefWindowProc 中,WM_PAINT 处理如下:
case WM_PAINT:
BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
return 0;
所以必须有这对函数。
绘制信息结构 PAINTSTRUCT
即 上例的ps
typedef struct tagPAINTSTRUCT {
HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT, *PPAINTSTRUCT, *NPPAINTSTRUCT, *LPPAINTSTRUCT;
程序只能使用前三个字段,其他供 Windows 内部使用
-
HDC 设备环境句柄
-
fErase 字段将被置为 FALSE(0),表示 Windows 在BeginPaint函数中已经擦除了无效区域的背景(用WNDCLASS 结构中 hbrBackground 指定的画刷)。
(如果想在窗口过程中自定义背景擦除方式,必须自己处理 WM_ERASEBKGND 消息。)调用 InvalidateRect() 函数使某矩形无效时,最后一个参数指定是否要擦除背景,如果是 FALSE(0),Windows 就不会擦除背景,此时BeginPaint函数后 PAINTSTRUCT 结构的 fRrase 是 TRUE(非0)。
-
rePaint 是一个RECT 类型的结构,定义无效矩形法的边界。(同时还是一个“裁剪”区,Windows 将本次绘制限制在这个区域里)
如果需要重绘整个客户区,可在 BeginPaint() 前调用 InvalidateRect() 函数使整个客户区无效。
注意:
处理 WM_PAINT 消息时,使用无效矩形以避免不必要的GDI 函数调用(否则绘制时,需要GDI函数自己恢复无效区域,影响程序运行效率)
1.2 获取设备环境句柄:方法二
hdc = GetDC(hwnd);
(使用 GDI 函数)
ReleaseDC(hwnd,hdc);
GetDC 返回设备环境句柄中的“裁剪”区是整个客户区,与无效矩形没有任何关系。也不会使无效区域有效化。
如果需要让整个客户区有效化:
ValidateRect(hwnd,NULL);
注意:
即使程序再处理非 WM_PAINT 消息时进行了绘制,他仍然必须收集足够的信息以便在收到 WM_PAINT 时能更新显示。
拓展1 ValidateRect 与 InvalidateRect
ValidateRect 使矩形范围内的区域有效化:
BOOL ValidateRect(hwnd,lpRect);//返回是否成功。
InvalidateRect 使矩形范围内的区域无效化:
BOOL InvalidateRect(hwnd,lpRect, bErase );
bErase——是否需要重绘该矩形区域,是(TRUE),否(FALSE)
拓展2 GetWindowDC()
获取整个窗口的设备环境句柄
意味着可以在窗口的标题栏输出,同样程序必须要处理 WM_NCPAINT(非客户区绘制)消息
2.TextOut 函数详解
最常用的字符输出函数 TextOut()
TextOut(hdc,x,y,psText,iLength);
hdc —— 设备环境句柄
psText —— 指向字符串的指针
(若 psText 指向的是Unicode字符串,则字符串占用的字节数将是 iLength 的两倍)
注意:
- 字符串不应该有任何控制字符
- ‘\0’ 并不一定是字符串结尾,它只认 iLength 指定的长度
- 因为设备环境中默认的映射模式是 MM_TEXT,逻辑单位和物理单位都是像素点,坐标原点是左上角,x从左往右增大,y从上到下增大
- 设备环境同时定义了一个裁剪区域,任何绘制只能在裁剪区域内,也只有在裁剪区域内的才会显示
注意:客户区的背景刷子不是设备环境的一部分。
3. 系统字体
设备环境同时定义了调用 TextOut 时Windows使用的字体,默认字体被称为“系统字体”或 SYSTEM_FONT,系统字体是 Windows 在标题栏,菜单和对话框中使用的默认字体。
现在的系统字体是变宽字体。
系统字体是一种“点阵字体”,每个字符由像素点构成。
4. 字符大小
可以通过 GetTextMetrics() 函数获得字体尺寸:
(最好在 WM_CREATE 消息中使用)
(具体操作看6)
TEXTMETRIC tm;
...
hdc = GetDC(hwnd);
GetTextMetrics(hdc,&tm);
ReleaseDC(hwnd,hdc);
该函数的第二个参数是一个 TEXTMETRIC 的结构,记录该设备环境中当前选定字体的信息:
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,所以它们以像素为单位。
5.文本尺寸的度量
5.1 高度
最重要的是 tmHeight:
tmHeight = tmAscent + tmDescent
内部间距(tmInternalLeading)包含在 tmAscent 中(也在 tmHeight中),该间距通常被用于显示重音符号。(可以设为0,但会使字符变短,以便包含重音符号)
还有外部间距(tmExternalLeading),不包含在 tmHeight 中。一般为0
5.2 宽度
小写字符的加权平均宽度(tmAveCharWidth)
字体中最宽字符的宽度(tmMaxCharWidth)
注:大写字符的平均宽度 = tmAveCharWidth * 1.5
而在等宽字体中cxCaps等于cxChar,可通过 tmPitchAndFamily 的最低位决定是否为等宽字体,1代表变宽,0代表等宽
注意:不要猜字体的尺寸!这与显示器,系统字体等有关。
6. 文本的格式化
6.1 字符尺寸的获取:
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;
6.2 格式化字符串
用 sprintf 或 wsprintf(Windows 版的sprintf)
这两个函数返回字符串长度
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);
可省略 iLength 的定义:
TextOut (hdc,x,y,szBuffer, wsprintf(szBuffer,TEXT("The sum of %i and %i is %i"),iA,iB,iA+iB));
6.3 文本显示对齐 SetTextAlign()
可改变文本显示的位置(左对齐等)
SetTextAlign(hdc,TA_TOP|TA_LEFT);
TextOut(...);
SetTextAlign(hdc,TA_TOP|TA_RIGHT);
拓展 获取客户区的尺寸
获取客户区的尺寸:
方法一(不好):
RECT rect;
...
GetClientRect(hwnd,&rect);
方法二(好):
static int cxClient,cyClient;
...
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0;
HIWORD 和 LOWORD 是两个宏,取高字(高16位)和低字(16低位)。
三,滚动条
-
注意相对关系:滚动条向下滑动,程序将文本相对于显示窗口向上移动。
-
主窗口样式中添加 WS_VSCROLL 或 WS_HSCROLL
-
客户区并不包括滚动条所占用的空间
-
特定的显示驱动和显示分辨率下的滚动条尺寸一样
-
可用 GetSystemMetrics 函数获取尺寸相关信息
-
没有自动的键盘接口,需自己编程实现
1. 滚动条的范围和位置
默认范围为 0~100(101个位置)
可更改范围:
SetScrollRange(hwnd,iBar,iMin,Max,bRedraw);
iBar 参数:SB_VERT 或 SB_HORZ
bRedraw 参数:需要重绘滚动条为 TRUE
(若还有其他需要修改显示的,请先设为 FALSE,避免过多的重绘)
获取范围:
GetScrollRange(hwnd,iBar,&iMin,$iMax,);
更改位置:
SetScrollPos(hwnd,iBar,iPos,bRedraw);;
获取位置:
iPos = GetScrollPos(hwnd,iBar);
使用窗口滚动条程序需负责的任务:
- 初始化滚动条的范围和位置
- 处理传送给窗口过程的滚动条消息
- 更新滑块的位置(Windows 不会自己更新滑块位置)
- 根据滚动条的变化更新客户区的内容
2.滚动条消息
用户单击滚动条或拖动滑块时,Windows 向窗口过程发送 WM_VSCROLL 消息 或 WM_HSCROLL 消息。
滚动条上的鼠标动作至少会产生两条消息,一条在鼠标按下时,另一条在鼠标松开时。
WM_VSCROLL 或 WM_HSCROLL 的消息参数:
- LOWORD(wParam) —— 通知码(具体动作):
通知码 | 值 | 说明 |
---|---|---|
SB_LINEUP | 0 | (垂直) |
SB_LINELEFT | 0 | (水平) |
SB_LINEDOWN | 1 | (垂直) |
SB_LINERIGHT | 1 | (水平) |
SB_PAGEUP | 2 | (垂直) |
SB_PAGELEFT | 2 | (水平) |
SB_PAGEDOWN | 3 | (垂直) |
SB_PAGERIGHT | 3 | (水平) |
SB_THUMBPOSITION | 4 | HIWORD(wParam)用户松开鼠标后滑块的最终位置 |
SB_THUMBTRACK | 5 | HIWORD(wParam)用户拖动滑块当前位置 |
SB_TOP | 6 | (垂直) |
SB_LEFT | 6 | (水平) |
SB_BOTTOM | 7 | (垂直) |
SB_RIGHT | 7 | (水平) |
SB_ENDSCROLL | 8 | 松开鼠标时(通常可以忽略) |
- 除 SB_THUMBPOSITION 和 SB_THUMBTRAC 的消息外,其余的 HIWORD(wParam) 被忽略
- 窗口滚动条的 lParam 参数为 0
若滚动条是应用程序窗口的一部分,就不会收到 通知码为:SB_TOP, SB_LEFT, SB_RIGHT, SB_BOTTOM。
若以 32位 整数作为滚动条范围虽然不多见,但是可行。需要通过调用 GetScrollInfo 函数获取范围和位置(wParam的高位字只是16位)
3.例 SYSTEMS2
//滑动滑块时窗口会闪,因为一直要重绘
//无法实现水平滚动条的使用
#include<Windows.h>
#include"systems.h"
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPreInstance,PSTR czCmdLine,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. 2"), //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;
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static int cxChar,cyChar,cxCaps,cxClient,cyClient,cxPage,cyPage,ixMaxPos,iyMaxPos,iMaxWidth,iPos;
int i,j,iBegPaint,iEndPaint;
TCHAR index[10],szAll[70];
PAINTSTRUCT ps;
HDC hdc;
TEXTMETRIC tm;
switch(message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
//Get size of char
GetTextMetrics(hdc,&tm);
cxChar=tm.tmAveCharWidth;
cyChar=tm.tmHeight +tm.tmExternalLeading;
cxCaps=(tm.tmPitchAndFamily&1?3:2)*cxChar/2;
//Get max width of text
iMaxWidth=22*cxCaps+40*cxChar;
iPos=0;
ReleaseDC(hwnd,hdc);
return 0;
case WM_VSCROLL:
switch(LOWORD(wParam))
{
case SB_LINEUP:
iPos--;
break;
case SB_LINEDOWN:
iPos++;
break;
case SB_THUMBTRACK:
iPos=HIWORD(wParam);
break;
case SB_PAGEUP:
iPos-=cyPage;
break;
case SB_PAGEDOWN:
iPos+=cyPage;
break;
}
iPos= max(0,min(iPos,iyMaxPos));
if(iPos!=GetScrollPos(hwnd,SB_VERT))
{
SetScrollPos(hwnd,SB_VERT,iPos,TRUE);
InvalidateRect(hwnd,NULL,TRUE);
return 0;
case WM_SIZE:
//Get size of client
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
//Set range of scroll
cxPage = cxClient/cxChar;
cyPage = cyClient/cyChar;
ixMaxPos = iMaxWidth/cxChar-cxPage+8;
iyMaxPos = NUMLINES -cyPage;
SetScrollRange(hwnd,SB_VERT,0,iyMaxPos,TRUE);
SetScrollRange(hwnd,SB_HORZ,0,ixMaxPos,TRUE);
return 0;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
iBegPaint = GetScrollPos(hwnd,SB_VERT);
iEndPaint = iBegPaint+cyPage;
for(i=iBegPaint;i<iEndPaint;i++)
{
TextOut(hdc,0,(i-iBegPaint)*cyChar,sysmetrics[i].szLabel,lstrlen(sysmetrics[i].szLabel));
TextOut(hdc,22*cxCaps,(i-iBegPaint)*cyChar,sysmetrics[i].szDesc ,lstrlen(sysmetrics[i].szLabel));
SetTextAlign(hdc,TA_TOP|TA_RIGHT);
TextOut(hdc,22*cxCaps+40*cxChar,(i-iBegPaint)*cyChar, index,wsprintf(index,TEXT("%5d"),GetSystemMetrics(sysmetrics[i].iIndex)));
SetTextAlign(hdc,TA_TOP|TA_LEFT);
//将一行字符都放在一个字符串中,企图实现水平滚动条,但是无法实现对齐,暂时放弃
/*szAll[0]=TEXT('\0');
lstrcat(szAll,sysmetrics[i].szLabel);
for(j=lstrlen(szAll);j<23;j++)
szAll[j]=TEXT(' ');
szAll[j]=TEXT('\0');
lstrcat(szAll,sysmetrics[i].szDesc);
for(j=lstrlen(szAll);j<63;j++)
szAll[j]=TEXT(' ');
szAll[j]=TEXT('\0');
wsprintf(index,TEXT("%-5d"),GetSystemMetrics(sysmetrics[i].iIndex));
lstrcat(szAll,index);
TextOut(hdc,0,(i-iBegPaint)*cyChar,szAll,lstrlen(szAll));*/
}
EndPaint(hwnd,&ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
DefWindowProc(hwnd,message,wParam,lParam);
}
}
4. 程序绘制代码的结构
-
程序应把所有客户区绘制工作都限制在处理 WM_PAINT
中。 -
在需要绘制时调用
InvalidateRect(hwnd,&Rect); UpdateWindow(hwnd);
5. 更好的滚动
滚动条信息函数
上述四个滚动条消息函数已经过时(尽管并不准确)
Win 32 API中引入的两个新的滚动条函数:
SetScrollInfo 和 GetScrollInfo
这两个函数有上述四个函数的所有功能,而且还加入了两个重要的新功能:
-
第一个新功能:滑块的大小
滑块的大小不应是固定的(例 SYSTEMS2的是固定的)
应有:
滑块大小/滚动条长度 = 页面大小/范围 = 文档显示数量/文档的总大小 -
得到32位的滑块范围或位置:
函数:
SetScrollInfo(hwnd,iBar,&si,bRedraw);
GetScrollInfo(hwnd,iBar,&si);
iBar 参数:SB_VERT 或 SB_HORZ 或 SB_CTL
bRedraw 参数:是否重绘
si :SCROLLINFO 的结构:
typedef struct tagSCROLLINFO
{
UINT cbSize;
UINT fMask;
int nMin;
int nMax;
UINT nPage;
int nPos;
int nTrackPos;
} SCROLLINFO, FAR *LPSCROLLINFO;
cbSize:在调用这两个函数之前,必须将 cbSize 设为该结构大小:
si.cbSize = sizeof(si);
或
si.cbSize = sizeof(SCROLLINFO);
fMask:“SIF”为前缀的标志,可是一个组合
SetScrollInfo() 中 fMask 指定 SIF_RANGE 时,必须在 nMin 和 nMax 中指定滚动条的范围。
GetScrollInfo() 中 fMask 指定 SIF_RANGE 时, nMin 和 nMax 返回滚动条的当前范围。
标志 | 说明 |
---|---|
SIF_RANGE | 范围 |
SIF_POS | 滑块位置 |
SIF_PAGE | 页面大小 |
SIF_TRACKPOS | 返回用户拖动的当前滑块位置(在处理通知码 SB_THUMBBACK 或 SB_THUMBPOSITION 的消息时 的GetScrollInfo函数中) |
SIF_DISABLENOSCROLL | 只用在 GetScrollInfo 中,原来不显示时禁用滚动条 |
SIF_ALL | SIF_RANGE |
SetScrollInfo 中 SIF_TRACKPOS被忽略
注意:
-
最远可以滚动的距离,最后一页的最后一行为文本的最后一行。
-
处理 WM_SIZE 时确定滚动条范围:
iVscrollMax = max(0,NUMLINES - cyClient/cyChar);
SetScrollRange(hwnd,SB_VERT,0,iVscrollMax,TRUE);
- 使用新的滚动条函数的另一个好处——
处理了上述最大滚动位置计算:
si.cbSize = sizeof(SCROLLINFO);
si.cbMask = SIF_RANGE|SIF_PAGE
si.nMin = 0;
si.nMax = NUMLINES - 1;
si.nPage = cyClient/cyChar;
SetScrollInfo(hwnd,SB_VERT,&si,TRUE):
这时滚动条的最大位置限制在 si.nMax - si.nPage +1
9. 内容的滚动实现
-
方法一:直接重绘客户区,显示要显示的内容。
-
方法二:用ScrollWindow 函数(新版本用ScrollWindowEx)
ScrollWindow(hwnd,cxPixels,cyPixels,lpRect,lpClipRect);
cxPixels:水平滚动的像素数
cyPixels:垂直滚动的像素数
lpRect:要滚动的客户区范围(NULL为整个客户区)
lpClipRect:要滚动的裁剪区(NULL为整个客户区)- Windows 自动将新滚动出现的地方无效化,
- 该函数并不是GDI函数
例 SYSTEMS3
#include<Windows.h>
#include"systems.h"
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);
int WINAPI WinMain (HINSTANCE hInstance,HINSTANCE hPreInstance,PSTR czCmdLine,int iCmdShow)
{
static TCHAR szAppName[] = TEXT("SysMets3");
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. 3"), //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;
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
static int cxChar,cyChar,cxCaps,cxClient,cyClient,cxPage,cyPage,ixMaxPos,iyMaxPos,iMaxWidth;
static SCROLLINFO si;
int i,j,iBegPaint,iEndPaint,iVertPos,iHorzPos;
TCHAR index[10],szAll[70];
PAINTSTRUCT ps;
HDC hdc;
TEXTMETRIC tm;
switch(message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
//Get size of char
GetTextMetrics(hdc,&tm);
cxChar=tm.tmAveCharWidth;
cyChar=tm.tmHeight +tm.tmExternalLeading;
cxCaps=(tm.tmPitchAndFamily&1?3:2)*cxChar/2;
//Get max width of text
iMaxWidth=22*cxCaps+40*cxChar;
ReleaseDC(hwnd,hdc);
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_LINEUP:
si.nPos--;
break;
case SB_LINEDOWN:
si.nPos++;
break;
case SB_THUMBTRACK:
si.nPos=si.nTrackPos;
break;
case SB_PAGEUP:
si.nPos-=si.nPage;
break;
case SB_PAGEDOWN:
si.nPos+=si.nPage;
break;
}
si.fMask=SIF_POS;
SetScrollInfo(hwnd,SB_VERT,&si,TRUE);
GetScrollInfo(hwnd,SB_VERT,&si);
if(iVertPos!=si.nPos)
{
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_LINEUP:
si.nPos--;
break;
case SB_LINEDOWN:
si.nPos++;
break;
case SB_THUMBTRACK:
si.nPos=si.nTrackPos;
break;
case SB_PAGEUP:
si.nPos-=si.nPage;
break;
case SB_PAGEDOWN:
si.nPos+=si.nPage;
break;
}
si.fMask=SIF_POS;
SetScrollInfo(hwnd,SB_HORZ,&si,TRUE);
GetScrollInfo(hwnd,SB_HORZ,&si);
if(iHorzPos!=si.nPos)
{
ScrollWindow(hwnd,cxChar*(iHorzPos-si.nPos),0,NULL,NULL);
UpdateWindow(hwnd);
}
case WM_SIZE:
//Get size of client
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
//Set range of scroll
si.cbSize=sizeof(si);
si.fMask=SIF_RANGE|SIF_PAGE;
si.nMax=NUMLINES-1;
si.nMin=0;
si.nPage=cyClient/cyChar;
SetScrollInfo(hwnd,SB_VERT,&si,TRUE);
si.fMask=SIF_RANGE|SIF_PAGE;
si.nMax=iMaxWidth/cxChar+2;
si.nMin=0;
si.nPage=cxClient/cxChar;
SetScrollInfo(hwnd,SB_HORZ,&si,TRUE);
return 0;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
si.cbSize=sizeof(si);
si.fMask=SIF_POS;
GetScrollInfo(hwnd,SB_VERT,&si);
iVertPos=si.nPos;
si.cbSize=sizeof(si);
si.fMask=SIF_POS;
GetScrollInfo(hwnd,SB_HORZ,&si);
iHorzPos=si.nPos;
iBegPaint = max(0,iVertPos+cyChar*ps.rcPaint.top);
iEndPaint = min(NUMLINES-1,iVertPos+cyChar*ps.rcPaint.bottom);
for(i=iBegPaint;i<iEndPaint;i++)
{
TextOut(hdc,cxChar*(1-iHorzPos),(i-iBegPaint)*cyChar,sysmetrics[i].szLabel,lstrlen(sysmetrics[i].szLabel));
TextOut(hdc,cxChar*(1-iHorzPos)+22*cxCaps,(i-iBegPaint)*cyChar,sysmetrics[i].szDesc ,lstrlen(sysmetrics[i].szLabel));
SetTextAlign(hdc,TA_TOP|TA_RIGHT);
TextOut(hdc,cxChar*(1-iHorzPos)+22*cxCaps+40*cxChar,(i-iBegPaint)*cyChar, index,wsprintf(index,TEXT("%5d"),GetSystemMetrics(sysmetrics[i].iIndex)));
SetTextAlign(hdc,TA_TOP|TA_LEFT);
}
EndPaint(hwnd,&ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
DefWindowProc(hwnd,message,wParam,lParam);
}
}