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
10LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
11
12int 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
55LRESULT 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
2LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
3
4int 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
47LRESULT 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
10LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
11
12int 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
55LRESULT 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。因而,程序随使用者在垂直滚动条上拖动卷动方块而垂直地滚动内容。这种想法很好,但应注意:一旦使用者发现程序会立即响应拖动的卷动方块,他们就会不断地来回拖动卷动方块。