完善的滚动条

完善的代码:
/*----------------------------------------------------
   SYSMETS3.C -- System Metrics Display Program No. 3
                 (c) Charles Petzold, 1998
  ----------------------------------------------------*/

#define WINVER 0x0500
#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.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 ("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) ;

               // Save the width of the three columns
          
          iMaxWidth = 40 * cxChar + 22 * cxCaps ;
          return 0 ;
          
     case WM_SIZE:
          cxClient = LOWORD (lParam) ;
          cyClient = HIWORD (lParam) ;

               // Set vertical scroll bar range and page size

          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) ;

               // Set horizontal scroll bar range and page size

          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:
               // Get all the vertial scroll bar information

          si.cbSize = sizeof (si) ;
          si.fMask  = SIF_ALL ;
          GetScrollInfo (hwnd, SB_VERT, &si) ;

               // Save the position for comparison later on

          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 ;         
          }
               // Set the position and then retrieve it.  Due to adjustments
               //   by Windows it may not be the same as the value set.

          si.fMask = SIF_POS ;
          SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
          GetScrollInfo (hwnd, SB_VERT, &si) ;

               // If the position has changed, scroll the window and update it

          if (si.nPos != iVertPos)
          {                    
               ScrollWindow (hwnd, 0, cyChar * (iVertPos - si.nPos), 
                                   NULL, NULL) ;
               UpdateWindow (hwnd) ;
          }
          return 0 ;
          
     case WM_HSCROLL:
               // Get all the vertial scroll bar information

          si.cbSize = sizeof (si) ;
          si.fMask  = SIF_ALL ;

               // Save the position for comparison later on

          GetScrollInfo (hwnd, SB_HORZ, &si) ;
          iHorzPos = si.nPos ;

          switch (LOWORD (wParam))
          {
          case SB_LINELEFT:
               si.nPos -= 1 ;
               break ;
               
          case SB_LINERIGHT:
               si.nPos += 1 ;
               break ;
               
          case SB_PAGELEFT:
               si.nPos -= si.nPage ;
               break ;
               
          case SB_PAGERIGHT:
               si.nPos += si.nPage ;
               break ;
               
          case SB_THUMBPOSITION:
               si.nPos = si.nTrackPos ;
               break ;
               
          default :
               break ;
          }
               // Set the position and then retrieve it.  Due to adjustments
               //   by Windows it may not be the same as the value set.

          si.fMask = SIF_POS ;
          SetScrollInfo (hwnd, SB_HORZ, &si, TRUE) ;
          GetScrollInfo (hwnd, SB_HORZ, &si) ;
          
               // If the position has changed, scroll the window 

          if (si.nPos != iHorzPos)
          {
               ScrollWindow (hwnd, cxChar * (iHorzPos - si.nPos), 0, 
                             NULL, NULL) ;
          }
          return 0 ;

     case WM_PAINT :
          hdc = BeginPaint (hwnd, &ps) ;

               // Get vertical scroll bar position

          si.cbSize = sizeof (si) ;
          si.fMask  = SIF_POS ;
          GetScrollInfo (hwnd, SB_VERT, &si) ;
          iVertPos = si.nPos ;

               // Get horizontal scroll bar position

          GetScrollInfo (hwnd, SB_HORZ, &si) ;
          iHorzPos = si.nPos ;

               // Find painting limits

          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) ;
}

这个程序相比之前,加入了水平滚动条和在拖动过程中客户区的变化,所以也就多了一些变量和对消息的处理。

我们先来看下 SCROLLINFO 这个结构体:

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,*LPSCROLLINFO;

为什么要引进这个结构体呢?

相对于 SetScrollRange(), SetScrollPos(), GetScrollRange(), GetScrollPos() 函数,

SetScrollInfo(), GetScrollInfo() 这两个函数更先进,因为它含有上述四个函数的所有功能,还加了两个新功能。

第一个功能是关于滑块的大小。滑块的大小应该和窗口文档的多少成比例。为了美观可以看如下的公式:

             滑块大小 / 滚动条长度  =  页面大小 / 范围  =  文档显示的数量 / 文档的总大小

这两个函数的语法如下:

             SetScrollRange(hwnd, iBar, &si, bRedraw);

             GetScrollRange(hwnd,iBar,&si);

可以看到有个 &si 这个东西。其实它就是 ScrollInfo结构体的变量。程序通常将该结构体变量这样命名。

同样的 iBar 也只能是 SB_VERT, SB_HORZ, SB_CTL 中的一个。SB_CTL 表示一个滚动条控件。

在调用这两个函数之前,必须将 cbSize 字段设为该结构体的大小:

                si.cbSize = sizeof(si);

          或者  si.cbSize = sizeof(SCROLLINFO);

可能很多老哥们都想到既然是个定值,那为啥要给第一个字段呢?

因为这样,以后的 Windows 版本可以扩展结构而同时保持与以前的应用程序兼容。

fMask 字段是一个或多个以 SIF 为前缀的标志。它们可以用 "位或" 运算组合在一起。

fMask: 指定结构中的哪些成员是有效,该值共有如下5种选择,可以选择多种用“OR”组合起来,该值在设置和查询参数时都必须填写。
SIF_ALL      :整个结构都有效
SIF_DISABLENOSCROLL:该值仅在设定参数时使用,视控件参数设定的需要来对本结构的成员进行取舍。
SIF_PAGE      :nPage成员有效
SIF_POS      :nPos成员有效
SIF_RANGE     :nMin和nMax成员有效
nMin:滚动范围最小值
nMax:滚动范围最大值
nPage:页尺寸,用来确定比例滚动框的大小
nPos:滚动框的位置
nTrackPos:拖动时滚动框的位置,该参数只能查询,不能设置。

在 SetScrollInfo() 中指定了 SIF_RANGE 时,必须在 nMin 和 nMax 中指定滚动条的范围。

                                        SIF_POS  时,必须在 nPos 字段指定滚动条的位置。

                                        SIF_PAGE 时,必须在 nPage 字段指定页面的大小。

                                     SIF_DISABLENOSCROLL, 作用原来让滚动条不显示的设置这时将禁用滚动条。( 该函数独有 )

在 GetScrollInfo() 中,指定了标志的,就返回相应的值。

                                       SIF_TRACKPOS 标志(该函数独有),而且只在处理通知码是 SB_THUMBTRACK 或

                                       SB_THUMBPOSITION 的 WM_VSCROLL 或 WM_HSCROLL 消息时。

                                       在函数返回时,SCROLLINFO结构的 nTrackPos 字段将返回当前滑块的位置(32位整数)。


   这里我们依赖 Windows 来维护滚动条信息和做边界检查。在处理 WM_VSCROLL 和 WM_HSCROLL 时

   首先需获取滚动条消息,根据通知码调整位置,然后调用 SetScrollInfo() 函数设置位置。程序然后调用

   GetScrollInfo()。如果在调用 SetScrollInof() 时超出了范围,Windows 将自动修正位置,并通过 GetScrollInfo()

   调用返回正确的位置。


ScrollWindow() 函数:

该函数滚动所指定的窗口客户区域内容。

BOOL ScrollWindow(HWND hWnd, int XAmount, int YAmount, CONST RECT *IpRect, CONST RECT *lpClipRect);

XAmount 指定水平滚动的距离,YAmount 指定垂直滚动的距离。

lpRect 指向 RECT 结构的指针,该结构指定了将要滚动的客户区范围。若此参数为NULL,则整个客户区域将被滚动。

lpClipRect 指向 RECT 结构的指针,该结构指定了要滚动的裁剪区域。只有这个矩形中的位才会被滚动。

在矩形之外的位不会被影响,即使它们是在lpRect矩形之内。假如lpClipRect为NULL,则不会在滚动矩形上进行裁剪



猜你喜欢

转载自blog.csdn.net/qq_41413835/article/details/80779709