lnuying

 

Windwos编程学习个人笔记-3 输出文字

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消息时,必须成对地呼叫BeginPaintEndPaint。如果窗口消息处理程序不处理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, 00))
 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, 00))
 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, 00))
 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。因而,程序随使用者在垂直滚动条上拖动卷动方块而垂直地滚动内容。这种想法很好,但应注意:一旦使用者发现程序会立即响应拖动的卷动方块,他们就会不断地来回拖动卷动方块。


posted on 2010-12-28 16:29 步天有术 阅读(408) 评论(0)  编辑 收藏 引用

导航

统计

常用链接

留言簿

随笔档案

收藏夹

Blog Super Man

搜索

最新评论

阅读排行榜

评论排行榜