<pre name="code" class="cpp">#include <windows.h>
#include "sysmets.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){
static TCHAR szAppName[] = TEXT("SysMets1");
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("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);
//执行视窗自我更新
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, cxCaps, cyChar;
HDC hdc;
INT i;
PAINTSTRUCT ps;
TCHAR szBuffer[10];
TEXTMETRIC tm;
switch(message){
case WM_CREATE:
hdc = GetDC(hwnd);
//GetDC 取得视窗的装置内容
GetTextMetrics(hdc, &tm);
//调用GetTextMetrics取得内定系统字体的文字大小
cxChar = tm.tmAveCharWidth;
//将平均字元宽度保存在cxChar中
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
//将大写字母的平均宽度保存在静态变量cxCaps中。对于固定宽度的字体,cxCaps等于cxChar,
//对于可变宽度字体,cxCaps等于cxChar*150%
cyChar = tm.tmHeight + tm.tmExternalLeading;
//将字元的总高度包括外部间距保存在cyChar中
ReleaseDC(hwnd, hdc);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
//调用BeginPaint取得装置内容代号句柄
for (i = 0; i < NUMLINES; i++){
//cyChar * i这个参数指定了字符串顶端相对于显示区域顶部的像素位置
TextOut(hdc, 0, cyChar * i, sysmetrics[i].szLabel, lstrlen(sysmetrics[i].szLabel));
//第一条TextOut在第一列显示了大写识别字。
//第二个参数为0,说明文字从显示区域的左边缘开始
//文字的内容来自sysmetrics结构的szLabel栏位,lstrlen来计算字符串的长度
TextOut(hdc, 22 * cxCaps, cyChar * i, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc));
//第二条TextOut显示了对系统尺寸值的描述,这些描述存放在sysmetrics结构的szDesc栏位中。
//第二个参数为22 * cxCaps, 第一列显示的最长的大写标识字有20个字元,因此第二列必须在第一列
文字开头
//向右20 * cxCaps处开始,使用22,以在两列之间多一些空余空间
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
//第三条TextOut显示从GetSystemMetrics函数取得的数值。变宽字体使得格式化向右对齐的数值有些
//棘手。那么,如果我们指定字符串结束的像素位置,而不是指定字符串的开始位置,以此向右对齐
//数值,是否会容易一点呢?
//指定后续函数的坐标将指定字符串的右上角,而不是左上角
//就是指定了字符串向右对齐的结束位置
TextOut(hdc, 22 * cxCaps + 40 * cxChar, cyChar * i, szBuffer, wsprintf(szBuffer, TEXT("%5d")
, GetSystemMetrics(sysmetrics[i].iIndex)));
SetTextAlign(hdc, TA_LEFT | TA_TOP);
//另一个对SetTextAlign的调用将对齐方式设定回普通方式
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
/*
TextOut(hdc, x, y, psText, iLength);hdc是装置内容代号(句柄)DC句柄psText参数是指向字符串的指针,iLength是字符串中字元的个数x是显示区域开始的水平位置,从左向右y是显示区域开始的垂直位置,从上向下TextOut(HDC hdc, 设备描述表句柄 int nXStart, 字符串的开始位置的x坐标int nYstart, 字符串的开始位置的y坐标LPCSTR lpString, 字符串int cbString) 字符串中字符的个数*/
#include <windows.h>
#include "sysmets.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){
static TCHAR szAppName[] = TEXT("SYSMETS2");
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.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszClassName = szAppName;
wndclass.lpszMenuName = NULL;
if (!RegisterClass(&wndclass)){
//注册视窗类别
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
//显示消息方块
return 0;
}
hwnd = CreateWindow(szAppName,
//视窗类别名字
TEXT("Get System Metrics NO.2"),
//视窗标题
WS_OVERLAPPEDWINDOW | WS_VSCROLL,
//视窗样式,使用卷动列,垂直卷动
CW_USEDEFAULT,
//initial x position
CW_USEDEFAULT,
//initial y position
CW_USEDEFAULT,
//initial x size
CW_USEDEFAULT,
//initial y size
NULL,
//父类视窗句柄
NULL,
//视窗菜单句柄
hInstance,
//程序实体句柄
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 int cxChar, cxCaps, cyChar, cyClient, iVscrollPos;
HDC hdc;
int i, y;
PAINTSTRUCT ps;
TCHAR szBuffer[10];
TEXTMETRIC tm;
switch(message){
case WM_CREATE:
hdc = GetDC(hwnd);
//取得视窗的装置内容
GetTextMetrics(hdc, &tm);
//调用GetTextMetrics取得内定系统字体的文字大小
cxChar = tm.tmAveCharWidth;
//将平均字元宽度保存在cxChar中
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
//将大写字母的平均宽度保存在静态变量cxCaps中。
//对于固定宽度的字体,cxCaps等于cxChar,对于可变宽度,cxCaps等于cxChar*150%
cyChar = tm.tmHeight + tm.tmExternalLeading;
//将字元的总高度包括外部间距保存在cyChar中
ReleaseDC(hwnd, hdc);
SetScrollRange(hwnd, SB_VERT, 0, NUMLINES - 1, false);
//设置卷动列的范围,垂直方向上,从0到NUMLINES-1,不根据范围重新画卷动列
SetScrollPos(hwnd, SB_VERT, iVscrollPos, true);
//设置卷动方块的位置,垂直方向上,位于iVscrollPos,根据范围重新画卷动列
return 0;
case WM_SIZE:
cyClient = HIWORD(lParam);
//显示区域的高度
return 0;
case WM_VSCROLL:
switch(LOWORD(wParam)){
//wParam的低字组是使用者拖动卷动方块时的目前位置
case SB_LINEUP:
//向上滑动
iVscrollPos -= 1;
//卷动方块的位置减1
break;
case SB_LINEDOWN:
//向下滑动
iVscrollPos += 1;
//卷动方块的位置加1
break;
case SB_PAGEUP:
//向上翻页
iVscrollPos -= cyClient/cyChar;
//卷动方块的位置减这一页能显示的行数
break;
case SB_PAGEDOWN:
//向下翻页
iVscrollPos += cyClient/cyChar;
//卷动方块的位置加这一页能显示的行数
break;
case SB_THUMBPOSITION:
//对于SB_THUMBPOSITION,新的卷动方块的位置是wParam的高字组
//wParam的高字组是使用者释放鼠标键后卷动方块的最终位置
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));
//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);
}
/*
GetClientRect函数来取得显示区域的大小。
确定视窗显示区域大小的更好地方法是在视窗消息处理程序中处理WM_SIZE消息。在视窗大小改变时,Windows给视窗
消息处理程序发送一个WM_SIZE消息。传给视窗消息处理程序的lParam参数的低字组中包含显示区域的宽度,高字组
中包含显示区域的高度。
要保存这些尺寸,要在视窗消息处理程序中定义两个静态变量,
static int cxClient, cyClient;
处理WM_SIZE的方法如下:
case WM_SIZE:
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
return 0;
在许多Windows程序中,WM_SIZE消息必然跟着一个WM_PAINT消息。因为在我们定义视窗类别时指定视窗类别样式为:
CS_HREDRAW | CS_VREDRAW
这种视窗类别样式告诉Windows,如果水平或者垂直大小发生改变,则强制更新显示区域。
用如下公式计算可以在显示区域内显示的文字的总行数:
cyClient / cyChar
如果显示区域的大小不足以容纳所有的内容,那么,知道视窗显示区域的大小只是为使用者提供了在显示区域内
卷动文字的第一步。
卷动列
可以用卷动列显示任何东西-无论是文字、图形、表格、资料库记录、图像或者网页,只要它所需的空间超过了视窗的
显示区域所能提供的空间,就可以使用卷动列。
卷动列有垂直方向和水平方向。
Windows文件和头文件标识字是依据使用者的观点:向上卷动意味着朝文件的开头移动;
只需要在CreateWindow的第三个参数中包括视窗样式(WS)是别字WS_VSCROLL(垂直卷动)和WS_HSCROLL即可。
Windows负责处理对卷动列的所有鼠标操作,但是,视窗卷动列没有自动的键盘界面。如果想用游标来完成卷动功能,
则必须提供这方面的代码。
每个卷动列均有一个相关的范围和位置,当卷动方块在卷动列的顶部时,卷动方块的位置是范围的最小值;
在卷动列的底部时,卷动方块的位置是范围的最大值。
setScrollRange(hwnd, iBar, iMin, iMax, bRedraw);
参数iBar为SB_VERT或者SB_HORZ,iMin和iMax分别是范围的最小值和最大值。
如果想要Windows根据新范围重新画卷动列,则设置bRedraw为true.
SetScrollPos(hwnd, iBar, iPos, bRedraw)
参数iPos是新位置
GetScrollRange和GetScrollPos来取得卷动列的目前范围和位置
在用鼠标单击卷动列或者拖动卷动方块时,Windows给视窗消息处理程序发送WM_VSCROLL和WM_HSCROLL消息。
在卷动列上的每个鼠标动作至少产生两个消息,一条在按下鼠标按钮,一条在释放按钮时产生。
当把鼠标的游标放在卷动方块上并按住鼠标键时,可以移动卷动方块。这样就产生了带有SB_THUMBTRACK和SB_THUMBPOSITION
通知码的卷动列消息。
*/
#include <windows.h>
#include "sysmets.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){
static TCHAR szAppName[] = TEXT("sysmets3");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hInstance = hInstance;
wndclass.lpfnWndProc = WndProc;
wndclass.lpszClassName = szAppName;
wndclass.lpszMenuName = NULL;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClass(&wndclass)){
MessageBox(hwnd, TEXT("Program requires Windows NT!"), szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName,
TEXT("Get System Metrics NO.3"),
WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
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 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){
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);
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_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);
}
/*
Win32API介绍的两个卷动列函数称为SetScrollInfo和GetScrollInfo。并且增加了两个特性
第一个功能涉及卷动方块的大小。卷动方块大小在SYSMETS2程序中是固定的。
这个函数中卷动方块大小与视窗中显示的文件大小成比例。显示的大小称为页面大小。
卷动方块大小/滚动长度 = 页面大小/范围 = 显示的文件数量/文件的总大小
可以使用SetScrollInfo来设置页面大小,从而设置了卷动方块的大小。
SetScrollInfo(hwnd, iBar, &si, bRedraw);
GetScrollInfo(hwnd, iBar, &si);
iBar参数是SB_VERT或者SB_HORZ,它还可以是用于卷动列控制的SB_STL.
bRedraw为true或者false,指出了是否要Windows重新绘制计算新资讯后的卷动列
typedef struct tagSCROLLINFO{
UINT cbSize; //set to sizeof(SCROLLINFO)
UINT fMask; //values to set or get
int nMin; //minimum range value
int nMax; //maximum range value
UINT nPage; //page size
int nPos; //current position
int nTrackPos; //current tracking position
};
SCROLLINFO, *PSCROLLINFO
在调用SetScrollInfo或者GetScrollInfo之前,必须将cbSize栏位设定为结构的大小:
si.cbSize = sizeof(si);
si.cbSize = sizeof(SCROLLINFO);
第一个参数指出结构的大小
fMask栏位设定为一个以上以SIF字首开头的旗标,并且可以使用C的位元操作OR运算子|组合这些旗标。
SetScrollInfo函数使用SIF_RANGE旗标时,必须把nMin和nMax参数设定为所需的卷动列范围
GetScrollInfo函数使用SIF_RANGE旗标时,必须把nMin和nMax参数设定为从函数传回的目前范围
SIF_POS位置
SIF_PAGE页面大小
新卷动列函数的一个好的功能是当使用与卷动列范围一样大的页面时,系统会隐藏卷动列,因为它并不需要。
主要是将资讯的最后一行显示在区域的底部而不是顶部即可。
Windows会把最大的卷动列位置限制为si.nMax - si.nPage + 1而不是si.nMax。
*/