C语言Windows程序设计滚动条

[来源] 达内    [编辑] 达内   [时间]2012-11-01

滚动条由滚动滑块以及两端的滚动箭头组成, 滚动条的作用是当需要显示的内容超过窗口客户区大小时提供上下/左右的翻页使用户能够完整的阅读显示信息

Windows滚动条介绍

   滚动条由滚动滑块以及两端的滚动箭头组成, 滚动条的作用是当需要显示的内容超过窗口客户区大小时提供上下/左右的翻页使用户能够完整的阅读显示信息, 滚动条的图示:

 

 

滚动条理论基础

  1>. 上下滚动?

    以垂直方向的滚动条为例, 当用户向下滚动滚动条时目的是想看到下方更多的的信息, 因此我们需要将下方的信息显示出来, 如何显示更多的信息?

    解决方案: 将不需要被显示的信息显示到客户区外 , 令信息自动被Windows截掉, 图示说明:

 

    由图示看出, 当用户向下翻动滚动条实际上我们是将起始输出部分的y坐标设为负数, 使已经显示过的信息输出到客户区的上部, 我们知道, 输出到客户区外部的信息会被Windows自动截掉, 所以用户不会再次看到已经显示过的信息, 取而代之的就是下方等待显示的信息, 上翻以及左右翻动的显示思路与下翻相同, 不再介绍。

 

  2>. 如何创建一个带有滚动条的窗口?

     创建带有水平/垂直的滚动条的窗口十分简单, 在 CreateWindow函数中说明下即可, CreateWindow函数的原型回顾:


  HWND CreateWindow(     LPCTSTR lpClassName,               //
窗口类名称

    LPCTSTR lpWindowName,              //
窗口标题

    DWORD dwStyle,                     //
窗口样式

    int
 x,                             //
窗口初始x坐标    int
 y,                             //
窗口初始y坐标    int
 nWidth,                        //
窗口初始x方向尺寸    int
 nHeight,                       //
窗口初始y方向尺寸    HWND hWndParent,                   //
父窗口句柄

    HMENU hMenu,                       //
窗口菜单句柄

    HANDLE hlnstance,                  //
程序实例句柄

    LPVOID lpParam                     //
创建参数

  );

     要窗口带有滚动条的窗口, 只需要在第三个参数

   DWORD dwStyle,                     //
窗口样式

      也就是在窗口样式的属性中使用位或( | )运算对相关的标识符进行组合即可得到一个带有垂直/水平滚动条的窗口,

        WS_HSCROLL    //
水平滚动条的标识符

        WS_VSCROLL    //
垂直滚动条的标识符                

     例如要创建一个既含有垂直滚动条又含有水平滚动条的组合:

    WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL

 

  3>. 对于滚动条, Windows需要做哪些事?

    当带有滚动条的窗口创建好后, Windows就会做一些必要的处理来协助我们更好的使用滚动条, Windows需要做的事情如下:

      1>. 处理滚动条中的所有鼠标消息;

      2>. 当用户单击滚动条时提供被单击部分的轻微闪烁;

      3>. 当用户拖动滑块时在滚动条内移动滑块;

      4>. 当窗口大小被调整时, 自动调整滚动条的长度;

      5>. 向滚动条所在的窗口发送滚动条的相关消息。

 

 

  4>. 我们要做的事情:

    相对于系统我们需要做的事情已经较为轻松了 主要有4项任务 :

      1>. 初始化滚动条的位置和滚动条的范围;

      2>. 处理系统发来的消息;

      3>. 根据发来的消息重置滑块的位置;

      4>. 根据滚动条消息重绘客户区显示的内容。

 

  5>. 将会收到哪些滚动条消息?

    滚动条消息来源同其他消息一样, 伴随着wParam与lParam消息机制, 当窗口为父窗口时消息的来源为wParam, 此时可忽略lParam的值, lParam用于子窗口消息。

    wParam参数分为两部分,&n bsp;高位字与低位字, 其中高位字代表用户松开鼠标键时滑块的最终位置, 低位字上代表鼠标在滚动条上的动作, 以一个值的形式表现出来, 同样, 为了方便记忆, 有不同的标识符对这些值进行区分, 这些标识符定义在WINUSER.H头文件中, 以SB_开头, 有关滚动条的消息标识符如下:

#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        

//释放鼠标

 

  6>.  需要使用到的新函数:

    ①. SetScrollRange

      SetScrollRange函数的作用是设置所指定滚动条范围的最小值和最大值, 其函数的原型如下:


  BOOL SetScrollRange(     HWND hWnd,            //


窗口句柄

    int nBar,              
//被设置的滚动条类型

    int nMinPos,           
//滚动条的最小位置

    int nMaxPos,           
//滚动条的最大位置

    BOOL bRedraw          //
重绘标志

  );

    参数二int nBar为被设置的滚动条类型, SB_HORZ表示该窗口的水平滚动条, SB_VERT表示垂直滚动条;

    参数四BOOL bRedraw指定滚动条是否被重绘以反映变化, 当参数为TRUE, 滚动条被重绘, FALSE则不被重绘。

 

    ②.  SetScrollPos

      SetScrollPos函数的作用是设置所指定滚动条中的滚动按钮的位置, 函数原型:

  int
 SetScrollPos(     HWND hWnd,     
//

窗体句柄

    int
 nBar,        //
被设置的滚动条类型    int
 nPos,           //
滚动条的新位置

     BOOL bRedraw      //
重绘标志   );

 

 

 

实战滚动条

  下面我们尝试着输出一些文字, 使其上下、左右均超过客户区的尺寸, 这样我们就可以实际练习下水平滚动条以及垂直滚动条了,  我们准备了很多行文字, 笔者也不知道到底有多少行, 而且最长的那行文字有多少个也不知道, 我们把他放在一个text.h头文件中, 并计算他到底有多少行以及最长的那行有多少字, 由于文字行数较多, 这里将它在代码框里折叠显示, 定义的头文件如下:

View Code - text.h

 

  在这个头文件中, 其中有两句是十分重要的, 一是计算总行数:

 #define
 NUMLINES ( (int)(sizeof(statement) / sizeof(statement[0]) ) )        //
计算总行数

  另一个是计算最长串字符个数的函数GetMaxLength, 该函数的定义如下:

int
 GetMaxLength() {     /*
     *计算statement所有句子中最长语句的长度     *返回值: int GetMaxLength(void) -> int     
*/

    int maxLength = 0
 ;     int
 i ;     for
( i = 0; i < NUMLINES; i++
 )     {         

if( wcslen(statement[i]) >
 maxLength )             maxLength 

= wcslen(statement[i]) ;     }     
return maxLength ; }

 

  下面编写我们的源文件, LearnScroll.c, 先看一下完整的代码, 稍后我们详细解释, 代码如下:

 

  1 #include<windows.h>
  2 #include"
text.h"

  3 
  4
 LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;        //
声明窗口过程函数  5
 

  6 int
 WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int
 iCmdShow )   7
 {   8
     

static TCHAR szAppName[] = TEXT("


LearnScroll") ; 
  9     HWND hwnd ; 
 10     MSG msg ; 
 11     WNDCLASS wndclass ; 
 12
 
 13     //
窗口类成员属性

 14     wndclass.lpfnWndProc =
 WndProc ;  15
      = CS_HREDRAW | CS_VREDRAW ; 
 16

     wndclass.hInstance = hInstance ; 
 17

     wndclass.lpszClassName = szAppName ; 
 18

     wndclass.lpszMenuName = NULL ; 
 19

     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH) ; 
 20

     wndclass.hCursor = LoadCursor(NULL, IDI_APPLICATION) ; 
 21

     wndclass.hIcon = LoadIcon(NULL, IDC_ARROW) ; 


 22     wndclass.cbClsExtra = 0


 ;  23
     wndclass.cbWndExtra = 0
 ;  24

 
 25     //
注册窗口类

 26     if
( !RegisterClass(&wndclass) ) 
 27

     {  28
         MessageBox( NULL, TEXT("
无法注册窗口类!

"), TEXT("
错误"
), MB_OK | MB_ICONERROR ) ; 
 29

         return 0
 ;  30
     }  31
 
 32     //
创建窗口

 33     hwnd = CreateWindow( 
 34         szAppName, TEXT(
"滚动条示例
"

),  35
         WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, 


 36         CW_USEDEFAULT, CW_USEDEFAULT, 
 37 
        CW_USEDEFAULT, CW_USEDEFAULT, 

 38 
        NULL, NULL, hInstance, NULL 

 39     ) ; 
 40
 
 41     //
显示窗口

 42 
    ShowWindow( hwnd, iCmdShow ) ;  43
 

    UpdateWindow( hwnd ) ;  44
 
 45     //
获取、翻译、分发消息

 46     while
( GetMessage( &msg, NULL, 0, 

0 ) )  47
     {  48
         TranslateMessage( &msg ) ; 
 49

         DispatchMessage( &msg ) ; 
 50

     }  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, cyClient, cxClient, iVscrollPos, iHscrollPos ;  58
     //
cxChar:平均字符宽度; cxCaps: 大写字母平均宽度; cyChar: 字符高; cyClient、cxClient: 客户区y、x方向尺寸;   59
     //

iVscrollPos: 竖直方向滚动条滑块位置; iHscrollPos: 水平方向滚动条滑块位置

 60 
 61     HDC hdc ; 
 62     RECT rect ;            
//

记录客户区RECT结构

 63     int
 i, x, y;        //
i循环控制, x记录水平方向坐标, y竖直方向坐标

 64     PAINTSTRUCT ps ; 
 65     TEXTMETRIC tm ; 
 66
 
 67     switch
(message)  68
     {  69
     case WM_CREATE:        //

处理WM_CREATE消息

 70         hdc =
 GetDC(hwnd) ;  71
         GetTextMetrics( hdc, &tm ) ;    //
获取系统字体信息 72
         cxChar = tm.tmAveCharWidth ;    //
获取平均宽度 73
         cxCaps = (tm.tmPitchAndFamily & 1
 ? 3 : 

2) * cxChar / 2 ;        
//大写字母平均宽度

 74
         cyChar = tm.tmHeight + tm.tmExternalLeading ;                    //
字符高度 75
 

        ReleaseDC( hwnd, hdc );  76
 
 77
         SetScrollRange( hwnd, SB_VERT, 0
, NUMLINES - 1

, FALSE ) ;        //
设置竖直滚动条范围的最小值和最大值 78
         SetScrollRange( hwnd, SB_HORZ, 0
, GetMaxLength() - 1

, FALSE ) ;    //
设置水平滚动条范围的最小值和最大值 79
         SetScrollPos( hwnd, SB_VERT, iVscrollPos, TRUE ) ;        //
设置竖直滚动条中的滚动按钮的位置
 80

         SetScrollPos( hwnd, SB_HORZ, iHscrollPos, TRUE ) ;        //
设置水平定滚动条中的滚动按钮的位置
 81
 
 82         return
 0 ; 
 83
 
 84     case
 WM_SIZE:        //
处理WM_SIZE 85
         GetClientRect( hwnd, &rect ) ; 
 86

         cyClient = rect.bottom ;            //
得到客户区y方向尺寸
 87

         cxClient = rect.right ;                //
得到客户区x方向尺寸
 88         return

 0 ; 
 89
 
 90     case
 WM_VSCROLL:    //
处理垂直滚动条消息 91
         switch

( LOWORD(wParam) )  92
         {  93
         case SB_LINEUP:            
//

上翻一行

 94             iVscrollPos -= 
1 ;  95
             break ; 
 96
 
 97         case
 SB_LINEDOWN:          //
下翻一行 98
             iVscrollPos += 

1 ;  99
             break ; 
100
 
101         case
 SB_PAGEUP:            //
向上翻一整页

102
             iVscrollPos -= cyClient / cyChar ; 
103

             break ; 
104

 
105         case
 SB_PAGEDOWN:          //
向下翻一整页

106
             iVscrollPos += cyClient / cyChar ; 
107

             break ; 
108

 
109         case
 SB_THUMBPOSITION:     //
拖动滑块滑块被放下时

110             iVscrollPos =
 HIWORD(wParam) ; 111
             

break ; 112
 
113         default
: 114
             break; 
115         } 
116         iVscrollPos = max( 
0

, min(iVscrollPos, NUMLINES -1
) ) ; 

117         if
( iVscrollPos != GetScrollPos(hwnd, SB_VERT) )        //


当滑块位置改变时重置滑块位置

118         { 
119 
            SetScrollPos( hwnd, SB_VERT, iVscrollPos, TRUE ) ; 120
             InvalidateRect( hwnd, NULL, TRUE ) ;                //
使客户区无效等待重绘
121         } 

122         return
 0 ; 
123
     
124     case
 WM_HSCROLL:    //
处理水平滚动条消息125
         switch

( LOWORD(wParam) ) 126
         { 127
         case SB_LINELEFT:        
//

左翻一行

128             iHscrollPos -= 
1 ; 129
             break ; 
130
 
131         case
 SB_LINERIGHT:        //
右翻一行132
             iHscrollPos += 

1 ; 133
             break ; 
134
 
135         case
 SB_PAGELEFT:        //
左翻一页136
             iHscrollPos -= cxClient / cxCaps ; 
137

             break ; 
138

 
139         case
 SB_PAGERIGHT:        //
右翻一页140
             iHscrollPos += cxClient / cxCaps ; 
141

             break ; 
142

 
143         case
 SB_THUMBPOSITION:    //
拖动滑块滑块被放下时

144             iHscrollPos =
 HIWORD(wParam) ; 145
             

break ;     146
         
147         default
: 148
             break ; 
149         } 
150         iHscrollPos = max( 
0

, min( iHscrollPos, GetMaxLength() -1

 ) ) ; 151         if
( iHscrollPos != GetScrollPos( hwnd, SB_HORZ ) ) 
152

         { 153
             SetScrollPos( hwnd, SB_HORZ, iHscrollPos, TRUE ) ; 
154

             InvalidateRect( hwnd, NULL, TRUE ) ; 
155         } 
156

         return 0
 ; 157
 
158     case
 WM_PAINT:        //
处理WM_PAINT消息159
         hdc = BeginPaint( hwnd, &ps ) ; 
160

 
161         for
( i= 0; i < NUMLINES; i++
 ) 

162         { 
163             y = cyChar * ( i -
iVscrollPos ) ; 

164             x = cxCaps * ( 
0

 - iHscrollPos ) ; 165
             TextOut( hdc, x, y, statement[i], lstrlen(statement[i]) ) ;        //
输出文字

166         } 
167
         
168         EndPaint( hwnd, &
ps ) ; 169         
return

 0 ; 
170
 
171     case
 WM_DESTROY:    //
处理WM_DESTROY消息172
         PostQuitMessage( 

0 ) ; 173
         return 0
 ; 174
     } 175
     
176     return
 DefWindowProc( hwnd, message, wParam, lParam ) ; 


177 }

 

  编译运行, 看下成果:

 

  看起来还算不错, 当滚动条向下翻时文字就随着向上滚动, 使下面的文字能够显示出来, 水平的滚动条也是这样, 下面详细说说重点部分的代码:

    1>. 创建一个带有垂直滚动条以及水平滚动条的窗口:

  hwnd = CreateWindow(         szAppName, TEXT(
"滚动条示例
"
),         WS_OVERLAPPEDWINDOW 

| WS_VSCROLL | WS_HSCROLL,         CW_USEDEFAULT, CW_USEDEFAULT,         CW_USEDEFAULT, CW_USEDEFAULT,         NULL, NULL, hInstance, NULL     ) ;

    

    2>. 设置垂直滚动条、水平滚动条的范围以及初始位置:

        SetScrollRange( hwnd, SB_VERT, 0
, NUMLINES - 1
, FALSE ) ;          //

设置垂直滚动条范围的最小值和最大值

        SetScrollRange( hwnd, SB_HORZ, 0
, GetMaxLength() - 1
, FALSE ) ;     //

设置水平滚动条范围的最小值和最大值

        SetScrollPos( hwnd, SB_VERT, iVscrollPos, TRUE ) ;              //
设置垂直滚动条中的滚动按钮的位置

        SetScrollPos( hwnd, SB_HORZ, iHscrollPos, TRUE ) ;              //
设置水平定滚动条中的滚动按钮的位置

      可以看出, 垂直滚动条的范围为0到行数-1, 这就意味这, 每下翻/上翻一个单位, 客户区显示的文字就会向上//向下滚动一行; 

    水平滚动条的范围为0到最长那行文字的长度-1,  每左翻/右翻一个单位, 客户区显示的文字就会向右//向左滚动一个字符的宽度

 

    3>. 当窗口大小调整时重新获取客户区尺寸数据:

      case WM_SIZE:        
//处理WM_SIZE

          GetClientRect( hwnd, &rect ) ;           cyClient 
= rect.bottom ;              //
得到客户区y方向尺寸          cxClient = rect.right ;                //
得到客户区x方向尺寸

          return 0
 ;

 

    4>. 处理滚动条消息:

      case WM_VSCROLL:    
//处理水平滚动条消息

          switch
( LOWORD(wParam) )           {         case
 滚动条消息:           [处理滚动条消息]           }

 

    5>. 重置滚动条滑块位置:

        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 ;    

      这一句注释上已经描述的是否清楚了, 当滑块位置改变时重置滑块位置并使客户区无效等待重绘。

 

    6>. 处理重绘消息:

    case
 WM_PAINT:        //
处理WM_PAINT消息          hdc = BeginPaint( hwnd, &ps ) ;            
for( i= 0
; i < NUMLINES; i++ )           {               y 
= cyChar * ( i -iVscrollPos ) ;               x 

= cxCaps * ( 0 -
 iHscrollPos ) ;               TextOut( hdc, x, y, statement[i], lstrlen(statement[i]) ) ;        //
输出文字

          }                    EndPaint( hwnd, 
&ps ) ;           return

 0 ;

 

      目的是重绘客户区内容并使其有效, 注意这里的

            y = cyChar * ( i -iVscrollPos ) ;             x 
= cxCaps * ( 0 - iHscrollPos ) ;

      这是计算从起始输出的坐标, 每行对应一个y值, 当滑块的位置向下滚动1时, y的值就会减去一个字符的高度, 使该行显示到窗口外部, 这样新的一行就会被显示出来, 同样, 翻动一整夜的计算思路同一行; x是指水平起始输出位置, 计算思路相同。

 

这样, 一个简单的带有滚动条的窗口就完成了! 看起来挺不错的, 不是吗? 可以先稍微休息一下, 下面我们还有事要做。

 

 

但这还不够好!

  在上面我们使用的滚动条中, 虽说能够滚动文字, 但是依然存在许多小问题:

     问题一滑块的大小是固定的, 而我见到的应用软件滑块能够根据内容的多少自动调整滑块大小;

     问题二:  当我拖动滑块时只有当滑块释放时页面才会滚动, 我想要的是当滑块被拖动时页面也同样跟着滚动;

    问题三:   当滑块滚动到底部时最后一行显示到了客户区顶部, 下面留有一大片的空白, 而我并不需要保留下面的空白, 也就是说最后一行在滑块拖动到底部后它只显示在底部就行。

 

  幸运的是, 我们依然有解决方案:

     问题一 :  自行设置滑块的大小;

      问题二 根据SB_THUMBTRACK消息处理页面的滚动, SB_THUMBTRACK消息是当滑块被拖动时就会源源不断的发来;

          问题三重新设置滚动的范围。

 

 

 

更好的滚动条:

  在使用更好的滚动条之前我们首先要认识三个新函数: SetScrollInfo、GetScrollInfo以及ScrollWindow.

  1>. SetScrollInfo

    函数功能: 用于设置滚动条的相关参数, 包括滚动范围的最大值和最小值, 页面大小, 滑块的位置, 函数的原型:

  int
 SetScrollInfo(     HWND hWnd;              


//窗口句柄

    int fnBar,               

//指定被设定参数的滚动条的类型

    LPSCROLLINFO lpsi,       //
指向一个SCROLLINFO结构

    BOOL fRedraw             //
重绘标志

  ) ;

  穿插讲述:  什么是SCROLLINFO结构?

    SCROLLINFO的成员记录有关滚动条的信息,  其结构定义如下 :

  typedef struct
 tagSCROLLINFO    {      UINT cbSize ;             //
设置为sizeof (SCROLLINFO), 表示该结构的大小

    UINT fMask ;             //
要设置或获取的值

    int
 nMin ;               //
滚动条范围的最小值    int
 nMax ;               //
滚动条范围的最大值    UINT nPage ;            //
页面大小

    int
 nPos ;               //
当前位置     int
 nTrackPos ;           //
当前追踪位置   }SCROLLINFO;

    成员一UINT cbSize :   该参数必须在函数调用之前设置,  cbSize表示该结构的大小, 用sizeof (SCROLLINFO)表示即可。

    成员二UINT fMask:  用于指定指定结构中的哪些成员是有效的, 通过位或运算进行组合 可组合的标识符如下 :

  SIF_ALL                     //
整个结构都有效
  SIF_DISABLENOSCROLL
//禁用滚动条   SIF_PAGE      // 用于指定或获取页面的大小, 在SetScrollInfo中用于设定页面的大小, 在GetScrollInfo用于获取页面的大小
  SIF_POS      
// 设置/取得滚动条滑块当前的位置
  SIF_RANGE     
//滚动条的范围   SIF_TRACKPOS // 仅在GetScrollInfo函数中使用, 并且仅用在处理SB_THUMBTRACK或者SB_THUMBPOSITION的WM_VSCROLL消息或WM_HSCROLL消息时使用。取得当前滑块的跟踪位置。

     穿插讲述完毕! 继续讲解第二个新函数。

 

  2>. GetScrollInfo

    用于取得滚动条的相关参数, 包括滚动范围的最大值和最小值, 页面大小, 滑块的位置, 函数的原型:


  BOOL GetScrollInfo(      HWND hWnd,                    //
窗口句柄

    int
 fnBar,                     //
指定被设定参数的滚动条的类型    LPSCROLLINFO lpsi              //
指向一个SCROLLINFO结构

   );

 

  3>. ScrollWindow

    该函数的作用是滚动所指定的窗口客户区域内容, 原型如下:


  BOOL ScrollWindow(     HWND hWnd,               //
窗口句柄

    int
 XAmount,                //
指定水平滚动的距离      int
 YAmount,                

//指定垂直滚动的距离

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

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

);

  好了, 说的差不多够多了, 研究代码才是更好的沟通方式, 下面我们实际实践一下更好的滚动条, 更多的细节请在代码中体会, 限于篇幅的长度, 这里将WinMain函数折叠显示, 仅将窗口过程函数部分的代码全部显示出来:

View Code - Function - WinMain

 

  窗口过程部分的代码:

  1
 LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) 
  2 { 


  3     static int
 cxChar, cxCaps, cyChar, cyClient, cxClient, iVscrollPos, iHscrollPos ; 
  4     //
cxChar:平均字符宽度; cxCaps: 大写字母平均宽度; cyChar: 字符高; cyClient、cxClient: 客户区y、x方向尺寸;  
  5

     //
iVscrollPos: 竖直方向滚动条滑块位置; iHscrollPos: 水平方向滚动条滑块位置

  6     HDC hdc ; 
  7     RECT rect ;            
//

记录客户区RECT结构

  8     int
 i, x, y;        //
i循环控制, x记录水平方向坐标, y竖直方向坐标

  9     PAINTSTRUCT ps ; 
 10     TEXTMETRIC tm ; 
 11     SCROLLINFO  si ;    
//

SCROLLINFO结构对象

 12     int
 iMaxLength ;    //
所有语句中的最大长度

 13 
 14
     iMaxLength = GetMaxLength() ;        //
取得最大长度 15 
 16     switch
(message)  17
     {  18
     case WM_CREATE:        //

处理WM_CREATE消息

 19         hdc =
 GetDC(hwnd) ;  20
         GetTextMetrics( hdc, &tm ) ;    //
获取系统字体信息 21
         cxChar = tm.tmAveCharWidth ;    //
获取平均宽度 22
         cxCaps = (tm.tmPitchAndFamily & 1
 ? 3 : 

2) * cxChar / 2 ;        
//大写字母平均宽度

 23
         cyChar = tm.tmHeight + tm.tmExternalLeading ;                    //
字符高度 24
 

        ReleaseDC( hwnd, hdc );  25

 26         return
 0 ; 
 27
 
 28     case
 WM_SIZE:        //
处理WM_SIZE 29
         GetClientRect( hwnd, &rect ) ; 
 30

         cxClient = LOWORD (lParam) ; 
 31

         cyClient = HIWORD (lParam) ; 
 32

 
 33         //
设置垂直滚动条相关参数

 34         si.cbSize = 
sizeof (si) ;  35
         si.fMask  = SIF_RANGE | SIF_PAGE ; 
 36

         si.nMin   = 0 ; 
 37
         si.nMax   = NUMLINES - 

1 ;  38
         si.nPage  = cyClient / cyChar ; 
 39

         SetScrollInfo(hwnd, SB_VERT, &si, TRUE) ; 
 40
 
 41         //
设置水平滚动条相关参数

 42         si.cbSize = 
sizeof (si) ;  43
         si.fMask  = SIF_RANGE | SIF_PAGE ; 
 44

         si.nMin   = 0 ; 
 45         si.nMax   = 2
 + iMaxLength ;  46
         si.nPage  = cxClient / cxCaps ; 
 47

         SetScrollInfo(hwnd, SB_HORZ, &si, TRUE) ; 
 48
 
 49         return
 0 ; 
 50
 
 51     case
 WM_VSCROLL:    //
处理垂直滚动条消息 52
         si.cbSize = 

sizeof (si) ;  53
         si.fMask  = SIF_ALL ; 
 54

         GetScrollInfo(hwnd, SB_VERT, &si) ; 


 55
 
 56
         iVscrollPos = si.nPos ;    //
记录当前滑块位置 57
 

 58         switch
( LOWORD(wParam) )    //
处理滚动条消息

 59         { 
 60         case
 SB_TOP:            //
到达顶部 61
             si.nPos =

 si.nMin ;  62             
break

 ;  63
                
 64         case
 SB_BOTTOM:            //
到达底部 65
             si.nPos =

 si.nMax ;  66             
break

 ;  67
 
 68         case
 SB_LINEUP:            //
上翻一行 69
             si.nPos -= 

1 ;  70
             break ; 
 71
 
 72         case
 SB_LINEDOWN:        //
下翻一行 73
             si.nPos += 

1 ;  74
             break ; 
 75
 
 76         case
 SB_PAGEUP:            //
向上翻一整页

 77             si.nPos -=
 si.nPage ;  78             

break ;  79
 
 80         case
 SB_PAGEDOWN:        //
向下翻一整页 81
             si.nPos +=

 si.nPage ;  82             

break ;  83
 
 84         case
 SB_THUMBTRACK:        //
移动滑块时 85
             si.nPos =

 si.nTrackPos ;  86
             

break ;  87
 
 88         default
:  89
             break; 
 90         } 
 91         si.fMask =
 SIF_POS ; 

 92
         SetScrollInfo(hwnd, SB_VERT, &si, TRUE) ;        //

重置滑块位置

 93
         GetScrollInfo(hwnd, SB_VERT, &si) ; 
 94         

if( si.nPos != iVscrollPos ) 
 95         {  96
                ScrollWindow(hwnd, 0
, cyChar * (iVscrollPos - si.nPos), NULL, NULL) ;        //
滚动内容 97
 

               UpdateWindow(hwnd) ;  98
         } 

 99         return
 0 ; 
100
     
101     case
 WM_HSCROLL:    //
处理水平滚动条消息102
         si.cbSize = 

sizeof (si) ; 103
         si.fMask  = SIF_ALL ; 
104

         GetScrollInfo (hwnd, SB_HORZ, &si) ; 


105
 
106
         iHscrollPos = si.nPos ;    //
记录当前滑块位置107
         switch

( LOWORD(wParam) ) 108
         { 109
         case SB_TOP:            
//

到达顶部

110             si.nPos =
 si.nMin ; 111             
break

 ; 112
                
113         case
 SB_BOTTOM:            //
到达底部114
             si.nPos =

 si.nMax ; 115             
break

 ; 116
             
117         case
 SB_LINELEFT:        //
左翻一行118
             si.nPos -= 

1 ; 119
             break ; 
120
 
121         case
 SB_LINERIGHT:        //
右翻一行122
             si.nPos += 

1 ; 123
             break ; 
124
 
125         case
 SB_PAGELEFT:        //
左翻一页126
             si.nPos -=

 si.nPage ; 127             

break ; 128
 
129         case
 SB_PAGERIGHT:        //
右翻一页130
             si.nPos +=

 si.nPage ; 131             

break ; 132
 
133         case
 SB_THUMBTRACK:        //
移动滑块时134
             si.nPos =

 si.nTrackPos ; 135
             

break ;     136
         
137         default
: 138
             break ; 
139         } 
140         si.fMask =
 SIF_POS ; 

141
         SetScrollInfo(hwnd, SB_HORZ, &si, TRUE) ;        //

重置滑块位置

142
         GetScrollInfo(hwnd, SB_HORZ, &si) ; 
143         

if( si.nPos != iHscrollPos ) 
144         { 145
                ScrollWindow(hwnd, cxCaps * (iHscrollPos - si.nPos), 0
, NULL, NULL) ;        //
滚动内容146 
               UpdateWindow(hwnd) ; 147
         } 

148         return
 0 ; 
149
 
150     case
 WM_PAINT:        //
处理WM_PAINT消息151
         hdc = BeginPaint( hwnd, &ps ) ; 
152

 
153         si.cbSize = 
sizeof (si) ; 154
         si.fMask  = SIF_POS ; 
155

         GetScrollInfo(hwnd, SB_VERT, &si) ; 


156         iVscrollPos = si.nPos ;                    //
获取当前垂直滑块位置

157 
158
         GetScrollInfo(hwnd, SB_HORZ, &si) ; 
159

         iHscrollPos = si.nPos ;                    //
获取当前水平滑块位置
160
 
161         for
( i= 0; i < NUMLINES; i++
 ) 

162         { 
163             y = cyChar * ( i -
 iVscrollPos ) ; 

164             x = cxCaps * ( 
0

 - iHscrollPos ) ; 165
             TextOut( hdc, x, y, statement[i], lstrlen(statement[i]) ) ;        //
输出文字

166         } 
167
         
168         EndPaint( hwnd, &
ps ) ; 169         
return

 0 ; 
170
 
171     case
 WM_DESTROY:    //
处理WM_DESTROY消息172
         PostQuitMessage( 

0 ) ; 173
         return 0
 ; 174
     } 175
     
176     return
 DefWindowProc( hwnd, message, wParam, lParam ) ; 


177 }

 

看一下成果:

嗯, 这样看起来就好多了, 如果嫌行间距太挤的话我们可以调节字符的高度

        cyChar = tm.tmHeight + tm.tmExternalLeading ;                    //
字符高度

使行间距增大些, 这样看起来会更舒服。

好了, 到这里, 一个较为完善的滚动条就完成了。

 

---------- ----------

 

wid, 2012.10.31

 

资源下载