Windwos编程学习个人笔记-3 输出文字
WM_PAINT消息
大多数Windows程序在WinMain中进入消息循环之前的初始化期间都要呼叫函数UpdateWindow。Windows利用这个机会给窗口消息处理程序发送第一个WM_PAINT消息。这个消息通知窗口消息处理程序:必须绘制显示区域。
在发生下面几种事件之一时,窗口消息处理程序会接收到一个WM_PAINT消息:
1。 在使用者移动窗口或显示窗口时,窗口中先前被隐藏的区域重新可见。
2。 使用者改变窗口的大小(如果窗口类别样式有着CS_HREDRAW和CS_VREDRAW位旗标的设定)。、
3。程序使用ScrollWindow或ScrollDC函数滚动显示区域的一部分。
4。 程序使用InvalidateRect或InvalidateRgn函数刻意产生WM_PAINT消息。
在某些情况下,显示区域的一部分被临时覆盖,Windows试图保存一个显示区域,并在以后恢复它,但这不一定能成功。在以下情况下,Windows可能发送WM_PAINT消息:
1。Windows擦除覆盖了部分窗口的对话框或消息框。
2。菜单下拉出来,然后被释放。
3。显示工具提示消息。
在某些情况下,Windows总是保存它所覆盖的显示区域,然后恢复它。这些情况是:
1。鼠标光标穿越显示区域。
2。图标拖过显示区域。
有效矩形(有效区域)和无效矩形(无效区域)
Windows每次绘图时是针对某个区域来进行的,这个区域就是无效区域。
在处理WM_PAINT消息处理期间,窗口消息处理程序在呼叫了BeginPaint之后,整个显示区域即变为有效。
GDI 简介
向窗口的显示区域写入字符串
TextOut (hdc, x, y, psText, iLength) hdc参数是「设备内容句柄」,它是GDI的重要部分; x和y参数定义了字符串在显示区域的开始位置; psText参数是指向字符串的指针,iLength是字符串的长度。
取得设备内容句柄:方法一
hdc = BeginPaint (hwnd, &ps) ;
使用GDI函数
EndPaint (hwnd, &ps) ;
处理WM_PAINT消息时,必须成对地呼叫
BeginPaint和
EndPaint。如果窗口消息处理程序不处理WM_PAINT消息,则它必须将WM_PAINT消息传递给Windows中DefWindowProc(内定窗口消息处理程序)。
ps---> 绘图信息结构
typedef struct tagPAINTSTRUCT
{
HDC hdc ; \\设备内容句柄
BOOL fErase ; \\在大多数情况下, fErase被标志为FALSE(0),这意味着Windows已经擦除了无效矩形的背景。
RECT rcPaint ; \\是RECT型态的结构(为left、top、right和bottom)
BOOL fRestore ;
BOOL fIncUpdate ;
BYTE rgbReserved[32] ;
} PAINTSTRUCT ;
使用者程序只使用前三个字段,其它字段由Windows内部使用。
如果程序通过呼叫Windows函数
InvalidateRect使显示区域中的矩形失效,则该函数的最后一个参数会指定是否擦除背景。如果这个参数为FALSE(即0),则Windows将不会擦除背景,并且在呼叫完BeginPaint后PAINTSTRUCT结构的fErase字段将为TRUE(非零)。
在处理WM_PAINT消息时,为了在更新的矩形外绘图,可以使用如下呼叫:
InvalidateRect (hwnd, NULL, TRUE) ;
该呼叫在BeginPaint呼叫之前进行,它使整个显示区域变为无效,并擦除背景。但是,如果最后一个参数等于FALSE,则不擦除背景,原有的东西将保留在原处。
取得设备内容句柄:方法二
要得到窗口显示区域的设备内容句柄,可以呼叫GetDC来取得句柄,在使用完后呼叫ReleaseDC:
hdc =
GetDC (hwnd) ;
使用GDI函数
ReleaseDC (hwnd, hdc) ;
与从BeginPaint传回设备内容句柄不同,GetDC传回的设备内容句柄具有一个剪取矩形,它等于整个显示区域。
与BeginPaint不同,GetDC不会使任何无效区域变为有效。如果需要使整个显示区域有效,可以呼叫
ValidateRect (hwnd, NULL) ;
一般可以呼叫GetDC和ReleaseDC来对键盘消息(如在字处理程序中)和鼠标消息(如在画图程序中)作出反应。此时,程序可以立刻根据使用者的键盘或鼠标输入来更新显示区域,而不需要考虑为了窗口的无效区域而使用WM_PAINT消息。不过,一旦确实收到了WM_PAINT消息,程序就必须要收集足够的信息后才能更新显示。
GetDC传回用于写入窗口显示区域的设备内容句柄,而
GetWindowDC传回写入整个窗口的设备内容句柄。
在需要确定文字大小时,先取得设备内容句柄,再呼叫GetTextMetrics:
hdc = GetDC (hwnd) ;
GetTextMetrics (hdc, &tm) ;
ReleaseDC (hwnd, hdc) ;
字体大小字体的纵向大小只由5个值确定
typedef struct tagTEXTMETRIC
{
LONG tmHeight ; // 表示了基准在线下字符的最大纵向高度,它是tmAscent和tmDescent的和。
LONG tmAscent ; //
LONG tmDescent ; //
LONG tmInternalLeading ; // 内部的间距 。tmInternalLeading字段可被设成0,在这种情况下,加重音的字母会稍稍缩短以便容纳重音符号。
LONG tmExternalLeading ; // 字设计者建议加在横向字符之间的空间大小。在安排文字行之间的空隙时,可以接受设计者建议的值,也可以拒绝它。
LONG tmAveCharWidth ; // 小写字母加权平均宽度
LONG tmMaxCharWidth ; // 字体中最宽字符的宽度
..... //其它结构字段
}
TEXTMETRIC, * PTEXTMETRIC ;
Windows启动后,系统字体就不会变化。可以在接受VM_CREATE消息时取得GetTexMetrics字体大小。VM_CREATE是在WinMain调用CreateWindow后发送的。
对于可变宽度字体,TEXTMETRIC结构中的tmPitchAndFamily字段的低位为1,对于固定宽度字体,该值为0。
大写字母的平均宽度为: cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
Windows函数
lstrlen来计算字符串的长度。
向右对齐
SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;之后,传给后续TextOut函数的坐标将指定字符串的右上角,而不是左上角。完成描画后在恢复为向左对齐。
示例1代码:
1
/**//*----------------------------------------------------
2
SYSMETS1.C -- System Metrics Display Program No. 1
3
(c) Charles Petzold, 1998
4
----------------------------------------------------*/
5
6
#define WINVER 0x0500
7
#include <windows.h>
8
#include "sysmets.h"
9
10
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
11
12
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
13
PSTR szCmdLine, int iCmdShow)
14

{
15
static TCHAR szAppName[] = TEXT ("SysMets1") ;
16
HWND hwnd ;
17
MSG msg ;
18
WNDCLASS wndclass ;
19
20
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
21
wndclass.lpfnWndProc = WndProc ;
22
wndclass.cbClsExtra = 0 ;
23
wndclass.cbWndExtra = 0 ;
24
wndclass.hInstance = hInstance ;
25
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
26
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
27
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
28
wndclass.lpszMenuName = NULL ;
29
wndclass.lpszClassName = szAppName ;
30
31
if (!RegisterClass (&wndclass))
32
{
33
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
34
szAppName, MB_ICONERROR) ;
35
return 0 ;
36
}
37
38
hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 1"),
39
WS_OVERLAPPEDWINDOW,
40
CW_USEDEFAULT, CW_USEDEFAULT,
41
CW_USEDEFAULT, CW_USEDEFAULT,
42
NULL, NULL, hInstance, NULL) ;
43
44
ShowWindow (hwnd, iCmdShow) ;
45
UpdateWindow (hwnd) ;
46
47
while (GetMessage (&msg, NULL, 0, 0))
48
{
49
TranslateMessage (&msg) ;
50
DispatchMessage (&msg) ;
51
}
52
return msg.wParam ;
53
}
54
55
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
56

{
57
static int cxChar, cxCaps, cyChar ;
58
HDC hdc ;
59
int i ;
60
PAINTSTRUCT ps ;
61
TCHAR szBuffer [10] ;
62
TEXTMETRIC tm ;
63
64
switch (message)
65
{
66
case WM_CREATE:
67
hdc = GetDC (hwnd) ;
68
69
GetTextMetrics (hdc, &tm) ;
70
cxChar = tm.tmAveCharWidth ;
71
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
72
cyChar = tm.tmHeight + tm.tmExternalLeading ;
73
74
ReleaseDC (hwnd, hdc) ;
75
return 0 ;
76
77
case WM_PAINT :
78
hdc = BeginPaint (hwnd, &ps) ;
79
80
for (i = 0 ; i < NUMLINES ; i++)
81
{
82
TextOut (hdc, 0, cyChar * i,
83
sysmetrics[i].szLabel,
84
lstrlen (sysmetrics[i].szLabel)) ;
85
86
TextOut (hdc, 22 * cxCaps, cyChar * i,
87
sysmetrics[i].szDesc,
88
lstrlen (sysmetrics[i].szDesc)) ;
89
90
SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
91
92
TextOut (hdc, 22 * cxCaps + 40 * cxChar, cyChar * i, szBuffer,
93
wsprintf (szBuffer, TEXT ("%5d"),
94
GetSystemMetrics (sysmetrics[i].iIndex))) ;
95
96
SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
97
}
98
EndPaint (hwnd, &ps) ;
99
return 0 ;
100
101
case WM_DESTROY :
102
PostQuitMessage (0) ;
103
return 0 ;
104
}
105
return DefWindowProc (hwnd, message, wParam, lParam) ;
106
}
107
108
WM_SIZE消息
在窗口大小改变时,Windows给窗口消息处理程序发送一个WM_SIZE消息,传给窗口消息处理程序的lParam参数的低字组中包含显示区域的宽度,高字组中包含显示区域的高度。
caseWM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0 ;
在显示区域内显示的文字的总行数:cyClient / cyChar
在显示区域内显示的文字的总列数:cxClient / cxChar
增加滚动条
在应用程序中包含水平或者垂直的滚动条,程序写作者只需要在CreateWindow的第三个参数中包括窗口样式(WS)标识符WS_VSCROLL(垂直卷动)和/或WS_HSCROLL(水平卷动)即可。滚动条在窗口中的宽度/高度是固定的,可以通过GetSystemMetrics来取得,参数传入SM_CXVSCROLL/SM_CXHSCROLL。
滚动条变动后,会发送WM_VSCROLL(上下滚动)和WM_HSCROLL(左右滚动),并且窗口消息处理程序( LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) )的wParam和lParam也带有信息,可以忽略lParam;它只用于作为子窗口而建立的滚动条(通常在对话框内)。wParam消息参数被分为一个低字组和一个高字组。wParam的低字组是一个数值,它指出了鼠标对滚动条进行的操作。这些操作在WINUSER.H中定义如下:
#define SB_LINEUP 0 //上滚一行
#define SB_LINELEFT 0 //左滚一行
#define SB_LINEDOWN 1 //下滚一行
#define SB_LINERIGHT 1 //右滚一行
#define SB_PAGEUP 2 //向上翻页
#define SB_PAGELEFT 2 //向左翻页
#define SB_PAGEDOWN 3 //向下翻页
#define SB_PAGERIGHT 3 //向右翻页
#define SB_THUMBPOSITION 4 //
#define SB_THUMBTRACK 5 //
#define SB_TOP 6 //最顶
#define SB_LEFT 6 //最左
#define SB_BOTTOM 7 //最下
#define SB_RIGHT 7 //最有
#define SB_ENDSCROLL 8 //在鼠标抬起后收到
押住滑块,移动滑块会收到SB_THUMBTRACK和SB_THUMBPOSITION通知。
在wParam的低字组是SB_THUMBTRACK时,wParam的高字组是使用者在拖动卷动方块时的目前位置,该位置位于卷动列范围的最小值和最大值之间。
在wParam的低字组是SB_THUMBPOSITION时,wParam的高字组是使用者释放鼠标键后卷动方块的最终位置。
对于其它的卷动列操作,wParam的高字组应该被忽略。
如果不通过呼叫SetScrollPos来处理SB_THUMBTRACK或SB_THUMBPOSITION消息,在使用者释放鼠标键后,卷动方块会迅速跳回原来的位置
设定滚动条的范围和位置
SetScrollRange (hwnd, iBar, iMin, iMax, bRedraw)
//参数iBar为SB_VERT或者SB_HORZ,iMin和iMax分别是范围的最小值和最大值,。如果想要Windows根据新范围重画滚动条,则设置bRedraw为TRUE(如果在呼叫SetScrollRange后,呼叫了影响滚动条位置的其它函数,则应该将bRedraw设定为FALSE以避免过多的重画)。
SetScrollPos (hwnd, iBar, iPos, bRedraw)
参数iPos是新位置,它必须在iMin至iMax的范围,位置总是离散的整数值.
GetScrollRang( HWND hWnd, int nBar, LPINT lpMinPos, LPINT lpMaxPos);)
取得滚动条的目前范围
GetScrollPos (HWND hWnd, int nBar)
取得滚动条的目前位置
示例2代码:
1
SysMets2
2
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
3
4
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
5
PSTR szCmdLine, int iCmdShow)
6

{
7
static TCHAR szAppName[] = TEXT ("SysMets2") ;
8
HWND hwnd ;
9
MSG msg ;
10
WNDCLASS wndclass ;
11
12
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
13
wndclass.lpfnWndProc = WndProc ;
14
wndclass.cbClsExtra = 0 ;
15
wndclass.cbWndExtra = 0 ;
16
wndclass.hInstance = hInstance ;
17
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
18
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
19
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
20
wndclass.lpszMenuName = NULL ;
21
wndclass.lpszClassName = szAppName ;
22
23
if (!RegisterClass (&wndclass))
24
{
25
MessageBox (NULL, TEXT ("This program requires Windows NT!"),
26
szAppName, MB_ICONERROR) ;
27
return 0 ;
28
}
29
30
hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 2"),
31
WS_OVERLAPPEDWINDOW | WS_VSCROLL,
32
CW_USEDEFAULT, CW_USEDEFAULT,
33
CW_USEDEFAULT, CW_USEDEFAULT,
34
NULL, NULL, hInstance, NULL) ;
35
36
ShowWindow (hwnd, iCmdShow) ;
37
UpdateWindow (hwnd) ;
38
39
while (GetMessage (&msg, NULL, 0, 0))
40
{
41
TranslateMessage (&msg) ;
42
DispatchMessage (&msg) ;
43
}
44
return msg.wParam ;
45
}
46
47
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
48

{
49
static int cxChar, cxCaps, cyChar, cyClient, iVscrollPos ;
50
HDC hdc ;
51
int i, y ;
52
PAINTSTRUCT ps ;
53
TCHAR szBuffer[10] ;
54
TEXTMETRIC tm ;
55
56
switch (message)
57
{
58
case WM_CREATE:
59
hdc = GetDC (hwnd) ;
60
61
GetTextMetrics (hdc, &tm) ;
62
cxChar = tm.tmAveCharWidth ;
63
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
64
cyChar = tm.tmHeight + tm.tmExternalLeading ;
65
66
ReleaseDC (hwnd, hdc) ;
67
68
SetScrollRange (hwnd, SB_VERT, 0, NUMLINES - 1, FALSE) ;
69
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
70
return 0 ;
71
72
case WM_SIZE:
73
cyClient = HIWORD (lParam) ;
74
return 0 ;
75
76
case WM_VSCROLL:
77
switch (LOWORD (wParam))
78
{
79
case SB_LINEUP:
80
iVscrollPos -= 1 ;
81
break ;
82
83
case SB_LINEDOWN:
84
iVscrollPos += 1 ;
85
break ;
86
87
case SB_PAGEUP:
88
iVscrollPos -= cyClient / cyChar ;
89
break ;
90
91
case SB_PAGEDOWN:
92
iVscrollPos += cyClient / cyChar ;
93
break ;
94
95
case SB_THUMBPOSITION:
96
iVscrollPos = HIWORD (wParam) ;
97
break ;
98
99
default :
100
break ;
101
}
102
103
iVscrollPos = max (0, min (iVscrollPos, NUMLINES - 1)) ;
104
105
if (iVscrollPos != GetScrollPos (hwnd, SB_VERT))
106
{
107
SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ;
108
InvalidateRect (hwnd, NULL, TRUE) ;
109
}
110
return 0 ;
111
112
case WM_PAINT:
113
hdc = BeginPaint (hwnd, &ps) ;
114
115
for (i = 0 ; i < NUMLINES ; i++)
116
{
117
y = cyChar * (i - iVscrollPos) ;
118
119
TextOut (hdc, 0, y,
120
sysmetrics[i].szLabel,
121
lstrlen (sysmetrics[i].szLabel)) ;
122
123
TextOut (hdc, 22 * cxCaps, y,
124
sysmetrics[i].szDesc,
125
lstrlen (sysmetrics[i].szDesc)) ;
126
127
SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
128
129
TextOut (hdc, 22 * cxCaps + 40 * cxChar, y, szBuffer,
130
wsprintf (szBuffer, TEXT ("%5d"),
131
GetSystemMetrics (sysmetrics[i].iIndex))) ;
132
133
SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
134
}
135
EndPaint (hwnd, &ps) ;
136
return 0 ;
137
138
case WM_DESTROY:
139
PostQuitMessage (0) ;
140
return 0 ;
141
}
142
return DefWindowProc (hwnd, message, wParam, lParam) ;
143
}
144
绘图程序的组织
在处理完滚动条消息后,SYSMETS2不更新显示区域,相反,它呼叫InvalidateRect使显示区域失效。这导致Windows将一个WM_PAINT消息放入消息队列中。
如果您希望立即更新无效区域,可以在呼叫InvalidateRect之后呼叫UpdateWindow:
UpdateWindow (hwnd) ;
如果显示区域的任一部分无效,则UpdateWindow将导致Windows用WM_PAINT消息呼叫窗口消息处理程序(如果整个显示区域有效,则不呼叫窗口消息处理程序)。这一WM_PAINT消息不进入消息队列,直接由Windows呼叫窗口消息处理程序。窗口消息处理程序完成更新后立即退出,Windows将控制传回给程序中UpdateWindow呼叫之后的叙述。
您可能注意到,UpdateWindow与WinMain中用来产生第一个WM_PAINT消息的函数相同。最初建立窗口时,整个显示区域内容变为无效,UpdateWindow指示窗口消息处理程序绘制显示区域。
建立更好的滚动,滚动条信息函数
Win32 API介绍的两个滚动条函数称作SetScrollInfo和GetScrollInfo。这些函数可以完成以前函数的全部功能,并增加了两个新特性
第一个功能涉及卷动方块的大小。GetScrollInfo函数增加了第二个重要的功能,或者说它改进了目前API的不足。假设您要使用65,536或更大单位的范围,这在16位Windows中是不可能的。当然在Win32中,函数被定义为可接受32位参数,因此是没有问题的。(记住如果使用这样大的范围,卷动方块的实际物理位置数仍然由卷动列的图素大小限制)
SetScrollInfo (hwnd, iBar, &si, bRedraw) ;
GetScrollInfo (hwnd, iBar, &si) ;
iBar参数是SB_VERT或SB_HORZ,SetScrollInfo的最后一个参数可以是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开头的一组宏控制:
SetScrollInfo函数使用SIF_RANGE旗标时,必须把nMin和nMax字段设定为所需的滚动条范围。GetScrollInfo函数使用SIF_RANGE旗标时,应把nMin和nMax字段设定为从函数传回的目前范围。
SIF_POS旗标也一样。当通过SetScrollInfo使用它时,必须把结构的nPos字段设为所需的位置。可以通过GetScrollInfo使用SIF_POS旗标来取得目前位置。
使用SIF_PAGE旗标能够取得页面大小。用SetScrollInfo函数把nPage设定为所需的页面大小。GetScrollInfo使用SIF_PAGE旗标可以取得目前页面的大小。如果不想得到比例化的滚动条,就不要使用该旗标。
当处理带有SB_THUMBTRACK或SB_THUMBPOSITION通知码的WM_VSCROLL或WM_HSCROLL消息时,通过GetScrollInfo只使用SIF_TRACKPOS旗标。从函数的传回中,SCROLLINFO结构的nTrackPos字段将指出目前的32位的卷动方块位置。
示例3代码:
1
/**//*----------------------------------------------------
2
SYSMETS3.C -- System Metrics Display Program No. 3
3
(c) Charles Petzold, 1998
4
----------------------------------------------------*/
5
6
#define WINVER 0x0500
7
#include <windows.h>
8
#include "sysmets.h"
9
10
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
11
12
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
13
PSTR szCmdLine, int iCmdShow)
14

{
15
static TCHAR szAppName[] = TEXT ("SysMets3") ;
16
HWND hwnd ;
17
MSG msg ;
18
WNDCLASS wndclass ;
19
20
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
21
wndclass.lpfnWndProc = WndProc ;
22
wndclass.cbClsExtra = 0 ;
23
wndclass.cbWndExtra = 0 ;
24
wndclass.hInstance = hInstance ;
25
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
26
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
27
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
28
wndclass.lpszMenuName = NULL ;
29
wndclass.lpszClassName = szAppName ;
30
31
if (!RegisterClass (&wndclass))
32
{
33
MessageBox (NULL, TEXT ("Program requires Windows NT!"),
34
szAppName, MB_ICONERROR) ;
35
return 0 ;
36
}
37
38
hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 3"),
39
WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
40
CW_USEDEFAULT, CW_USEDEFAULT,
41
CW_USEDEFAULT, CW_USEDEFAULT,
42
NULL, NULL, hInstance, NULL) ;
43
44
ShowWindow (hwnd, iCmdShow) ;
45
UpdateWindow (hwnd) ;
46
47
while (GetMessage (&msg, NULL, 0, 0))
48
{
49
TranslateMessage (&msg) ;
50
DispatchMessage (&msg) ;
51
}
52
return msg.wParam ;
53
}
54
55
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
56

{
57
static int cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth ;
58
HDC hdc ;
59
int i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd ;
60
PAINTSTRUCT ps ;
61
SCROLLINFO si ;
62
TCHAR szBuffer[10] ;
63
TEXTMETRIC tm ;
64
65
switch (message)
66
{
67
case WM_CREATE:
68
hdc = GetDC (hwnd) ;
69
70
GetTextMetrics (hdc, &tm) ;
71
cxChar = tm.tmAveCharWidth ;
72
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2 ;
73
cyChar = tm.tmHeight + tm.tmExternalLeading ;
74
75
ReleaseDC (hwnd, hdc) ;
76
77
// Save the width of the three columns
78
79
iMaxWidth = 40 * cxChar + 22 * cxCaps ;
80
return 0 ;
81
82
case WM_SIZE:
83
cxClient = LOWORD (lParam) ;
84
cyClient = HIWORD (lParam) ;
85
86
// Set vertical scroll bar range and page size
87
88
si.cbSize = sizeof (si) ;
89
si.fMask = SIF_RANGE | SIF_PAGE ;
90
si.nMin = 0 ;
91
si.nMax = NUMLINES - 1 ;
92
si.nPage = cyClient / cyChar ;
93
SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
94
95
// Set horizontal scroll bar range and page size
96
97
si.cbSize = sizeof (si) ;
98
si.fMask = SIF_RANGE | SIF_PAGE ;
99
si.nMin = 0 ;
100
si.nMax = 2 + iMaxWidth / cxChar ;
101
si.nPage = cxClient / cxChar ;
102
SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
103
return 0 ;
104
105
case WM_VSCROLL:
106
// Get all the vertial scroll bar information
107
108
si.cbSize = sizeof (si) ;
109
si.fMask = SIF_ALL ;
110
GetScrollInfo (hwnd, SB_VERT, &si) ;
111
112
// Save the position for comparison later on
113
114
iVertPos = si.nPos ;
115
116
switch (LOWORD (wParam))
117
{
118
case SB_TOP:
119
si.nPos = si.nMin ;
120
break ;
121
122
case SB_BOTTOM:
123
si.nPos = si.nMax ;
124
break ;
125
126
case SB_LINEUP:
127
si.nPos -= 1 ;
128
break ;
129
130
case SB_LINEDOWN:
131
si.nPos += 1 ;
132
break ;
133
134
case SB_PAGEUP:
135
si.nPos -= si.nPage ;
136
break ;
137
138
case SB_PAGEDOWN:
139
si.nPos += si.nPage ;
140
break ;
141
142
case SB_THUMBTRACK:
143
si.nPos = si.nTrackPos ;
144
break ;
145
146
default:
147
break ;
148
}
149
// Set the position and then retrieve it. Due to adjustments
150
// by Windows it may not be the same as the value set.
151
152
si.fMask = SIF_POS ;
153
SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
154
GetScrollInfo (hwnd, SB_VERT, &si) ;
155
156
// If the position has changed, scroll the window and update it
157
158
if (si.nPos != iVertPos)
159
{
160
ScrollWindow (hwnd, 0, cyChar * (iVertPos - si.nPos),
161
NULL, NULL) ;
162
UpdateWindow (hwnd) ;
163
}
164
return 0 ;
165
166
case WM_HSCROLL:
167
// Get all the vertial scroll bar information
168
169
si.cbSize = sizeof (si) ;
170
si.fMask = SIF_ALL ;
171
172
// Save the position for comparison later on
173
174
GetScrollInfo (hwnd, SB_HORZ, &si) ;
175
iHorzPos = si.nPos ;
176
177
switch (LOWORD (wParam))
178
{
179
case SB_LINELEFT:
180
si.nPos -= 1 ;
181
break ;
182
183
case SB_LINERIGHT:
184
si.nPos += 1 ;
185
break ;
186
187
case SB_PAGELEFT:
188
si.nPos -= si.nPage ;
189
break ;
190
191
case SB_PAGERIGHT:
192
si.nPos += si.nPage ;
193
break ;
194
195
case SB_THUMBPOSITION:
196
si.nPos = si.nTrackPos ;
197
break ;
198
199
default :
200
break ;
201
}
202
// Set the position and then retrieve it. Due to adjustments
203
// by Windows it may not be the same as the value set.
204
205
si.fMask = SIF_POS ;
206
SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
207
GetScrollInfo (hwnd, SB_HORZ, &si) ;
208
209
// If the position has changed, scroll the window
210
211
if (si.nPos != iHorzPos)
212
{
213
ScrollWindow (hwnd, cxChar * (iHorzPos - si.nPos), 0,
214
NULL, NULL) ;
215
}
216
return 0 ;
217
218
case WM_PAINT :
219
hdc = BeginPaint (hwnd, &ps) ;
220
221
// Get vertical scroll bar position
222
223
si.cbSize = sizeof (si) ;
224
si.fMask = SIF_POS ;
225
GetScrollInfo (hwnd, SB_VERT, &si) ;
226
iVertPos = si.nPos ;
227
228
// Get horizontal scroll bar position
229
230
GetScrollInfo (hwnd, SB_HORZ, &si) ;
231
iHorzPos = si.nPos ;
232
233
// Find painting limits
234
235
iPaintBeg = max (0, iVertPos + ps.rcPaint.top / cyChar) ;
236
iPaintEnd = min (NUMLINES - 1,
237
iVertPos + ps.rcPaint.bottom / cyChar) ;
238
239
for (i = iPaintBeg ; i <= iPaintEnd ; i++)
240
{
241
x = cxChar * (1 - iHorzPos) ;
242
y = cyChar * (i - iVertPos) ;
243
244
TextOut (hdc, x, y,
245
sysmetrics[i].szLabel,
246
lstrlen (sysmetrics[i].szLabel)) ;
247
248
TextOut (hdc, x + 22 * cxCaps, y,
249
sysmetrics[i].szDesc,
250
lstrlen (sysmetrics[i].szDesc)) ;
251
252
SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
253
254
TextOut (hdc, x + 22 * cxCaps + 40 * cxChar, y, szBuffer,
255
wsprintf (szBuffer, TEXT ("%5d"),
256
GetSystemMetrics (sysmetrics[i].iIndex))) ;
257
258
SetTextAlign (hdc, TA_LEFT | TA_TOP) ;
259
}
260
261
EndPaint (hwnd, &ps) ;
262
return 0 ;
263
264
case WM_DESTROY :
265
PostQuitMessage (0) ;
266
return 0 ;
267
}
268
return DefWindowProc (hwnd, message, wParam, lParam) ;
269
}
270
上例代码Ln153Ln154(Ln206.Ln207)这样Set后又Get是利用了Windows纠错,SetScrollInfo如果有越界信息,GetScrollInfo传回的是正确的值。
ScrollWindow函数在窗口的显示区域中滚动信息而不是重画它。ScrollWindow的最后两个参数设定为NULL,这指出了要卷动整个显示区域。这指出了要卷动整个显示区域。Windows自动把显示区域中未被卷动操作覆盖的矩形设为无效。这会
产生WM_PAINT消息。再也不需要InvalidateRect了。注意ScrollWindow不是GDI函数,因为它不需设备内容句柄。
WM_HSCROLL处理拦截SB_THUMBPOSITION通知码并忽略SB_THUMBTRACK。因而,如果使用者在水平滚动条上拖动卷动方块,在使用者释放鼠标按钮之前,程序不会水平卷动窗口的内容。
WM_VSCROLL的方法与之不同:程序拦截SB_THUMBTRACK消息并忽略SB_THUMBPOSITION。因而,程序随使用者在垂直滚动条上拖动卷动方块而垂直地滚动内容。这种想法很好,但应注意:一旦使用者发现程序会立即响应拖动的卷动方块,他们就会不断地来回拖动卷动方块。