第四章 文本输出 (2.滚动条)

论坛 期权论坛 脚本     
匿名技术用户   2020-12-30 21:20   42   0

range 范围

在应用程序的窗口中包括滚动条相当容易。只需要在CreateWindows的第三个参数中包括窗口风格标识符WS_VSCROLL (垂直滚动条) 或WS_HSCROLL(水平滚动条)或者同时包括两者。客户区并不包含滚动条所占的空间。

滚动条的范围和位置

滚动条的范围是一对整数,分别代表滚动条的最小值和最大值。

位置:指滑块在范围中所处的值。当滑块在滚动条的顶端(或最左)时,滑块的位置是范围的最小值。

相应的,当滑块在滚动条的底部(或最右)时,位置是范围的最大值。

SetScrollRange (hwnd, iBar, iMin, iMax, bRedraw) ;

iBar参数 要么是SB_VERT 要么是SB_HORZ

iMin、iMax 分别对应范围的最小值 、最大值

需要Windows根据新的范围来重绘滚动条时,请将bReadraw设为TRUE

( 如果在调用SetScrollRange函数之后还将调用其他函数来调整滚动条的显示时,最好将bReadraw设为FALSE以避免过多的重绘)

通过 SetScrollPos 函数用来指定滑块在滚动条范围中的位置:

SetScrollPos( hwnd, iBar, iPos, bRearaw ) ;

参数iPos是滑块的新位置,它必须在iMiniMax之间。

Windows提供了两个类似的函数(GetScrollRange)和(GetScrollPos)用于获取滚动条的当前范围和位置。

在程序中使用滚动条时,程序需要和Windows共同负责维护滚动条以及滑块在滚动条中的位置。

程序需要处理的任务:

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

2.处理传送给窗口过程的滚动条消息。

3.更新滑块的位置

4.根据滚动条的变化更新客户区的内容

滚动条消息

wParam参数被分为低位字和高位字。

wParam的低位字代表了鼠标在滚动条上的动作。这个值被称为“通知码”,由一个SB开头的标识符定义(SB代表滚动条)。

下面是在WINUSER.H中定义的通知码:

/*
 * Scroll Bar Commands
 */
#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_ENDSCROLL通知码的消息。

程序通常可以忽略带SB_ENDSCROLL的消息。Windows并不会自己改变滑块的位置,应用程序需要调用SetScrollPos函数来改变它。

将鼠标放在滑块上然后按下鼠标键时,可以移动滑块。这将会生成带SB_THUMBTRACK 和 SB_THUMBPOSITION通知码的滚动条消息。

当wParam的低位字是SB_THUMBTRACK时,wParam的高位字是用户拖动滑块的当前位置。这个位置处于滚动条范围的最小值和最大值之间

当wParam的低位字是SB_THUMBPOSITION 时,wParam的高位字是用户松开鼠标键时滑块的最终位置。

对于其他的滚动条动作,wParam的高位字应被忽略。

WinMain函数中的处理:

hwnd = CreateWindow (szAppName, TEXT ("Get System Metrics No. 2"),
                          WS_OVERLAPPEDWINDOW | WS_VSCROLL, //WS_VSCROLL 表示垂直滚动条
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          CW_USEDEFAULT, CW_USEDEFAULT,
                          NULL, NULL, hInstance, NULL) ;

窗口过程中的处理:

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static int  cxChar, cxCaps, cyChar, cyClient, iVscrollPos ;
     HDC         hdc ;
     int         i, y ;
     PAINTSTRUCT ps ;
     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) ;

          SetScrollRange (hwnd, SB_VERT, 0, NUMLINES - 1, FALSE) ;  //设置滚动条的范围
          SetScrollPos   (hwnd, SB_VERT, iVscrollPos, TRUE) ;//设置滑块的初始位置,但是iVscrollPos没有赋值啊???不懂系统自动处理?
          return 0 ;

     case WM_SIZE:
          cyClient = HIWORD (lParam) ; //处理客户区的高度
           return 0 ;

     case WM_VSCROLL:
          switch (LOWORD (wParam))
          {
          case SB_LINEUP:
               iVscrollPos -= 1 ;
               break ;
     
          case SB_LINEDOWN:
               iVscrollPos += 1 ;
               break ;
     
          case SB_PAGEUP:
               iVscrollPos -= cyClient / cyChar ; //客户区的高度/字体高度 = 客户区内字的 行数。实现翻页操作
               break ;
     
          case SB_PAGEDOWN:
               iVscrollPos += cyClient / cyChar ;
               break ;
     
          case SB_THUMBPOSITION:
               iVscrollPos = HIWORD (wParam) ; //直接移到当前滑块的位置
               break ;
     
          default :
               break ;
          }

          iVscrollPos = max (0, min (iVscrollPos, NUMLINES - 1)) ;//确保新滑块的位置在滚动条范围之内

          if (iVscrollPos != GetScrollPos (hwnd, SB_VERT))//获取滑块位置与当前位置比较。
          {
               SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE) ; //设置新滑块的位置
               InvalidateRect (hwnd, NULL, TRUE) ; //使窗口无效重绘,WINDOWS发送一条WM_PAINT消息
          }    //可以立即调用UpdateWindow(hwnd); 因为WM_PAINT优先级比较低
          return 0 ;

     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
     
          for (i = 0 ; i < NUMLINES ; i++)
          {
               y = cyChar * (i - iVscrollPos) ; //上翻 或下翻的行数
     
               TextOut (hdc, 0, y,
                        sysmetrics[i].szLabel,
                        lstrlen (sysmetrics[i].szLabel)) ;
     
               TextOut (hdc, 22 * cxCaps, y,
                        sysmetrics[i].szDesc,
                        lstrlen (sysmetrics[i].szDesc)) ;
     
               SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
     
               TextOut (hdc, 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) ;
}

运行结果:

改进的滚动条:

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
 static int cxChar, cxCaps, cyChar, cyClient, iVscrollPos,iVscrollMax;
 HDC hdc;
 int  i, y;
 PAINTSTRUCT ps;
 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);

  SetScrollRange(hwnd, SB_VERT,0,NUMLINES-1, FALSE);
  SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);
  
  return 0;
 case WM_SIZE:
  cyClient = HIWORD(lParam); // lParam高位字代表客户区的高度
  iVscrollMax = max(0, NUMLINES-cyClient/cyChar+1);  //注:窗口的客户区发生改变 cyClient / cyChar值的改变
  SetScrollRange(hwnd,SB_VERT,0,iVscrollMax,TRUE);
  return 0;
 case WM_VSCROLL:
  switch(LOWORD(wParam))  // wParam低位字代表鼠标在滚动条上的动作
  {
  case SB_LINEUP:
   iVscrollPos -= 1;
   break;
  case SB_LINEDOWN:
   iVscrollPos += 1;
   break;
  case SB_PAGEUP:
   iVscrollPos -= cyClient/cyChar;
   break;
  case SB_PAGEDOWN:
   iVscrollPos += cyClient/cyChar;
   break;
  case SB_THUMBPOSITION:
   iVscrollPos = HIWORD(wParam);
   break;
  default:
   break;
  }
  iVscrollPos = max (0,min(iVscrollPos,NUMLINES-cyClient/cyChar+1));
  if(iVscrollPos != GetScrollPos(hwnd,SB_VERT))
  {
   SetScrollPos(hwnd,SB_VERT,iVscrollPos,TRUE);
   InvalidateRect(hwnd,NULL,TRUE);
  // UpdateWindow(hwnd);  //立刻更新无效区域
  }
  return 0;

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

   TextOut(hdc,0,y,
     sysmetrics[i].szLabel,
     strlen(sysmetrics[i].szLabel));
   
   TextOut(hdc,22*cxCaps,y,
      sysmetrics[i].szDesc,
     lstrlen(sysmetrics[i].szDesc));
   
   SetTextAlign(hdc,TA_RIGHT|TA_TOP);

   TextOut(hdc,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);
}
运行结果:

效果更好的滚动条

SetScrollRang 、 GetScrollRang 、 GetScrollPos 、 GetScrollPos 用法简单,滑块的大小固定,自WINDOWS 1.0 就已经存在。
下面将使用WIN32中独有的一些新函数: SetScrollInfo 和 GetScrollInfo 这两个函数有上面的所有功能,还加入了两个重要的新功能。

第一个新功能就是关于滑块的大小。

另一重要的功能是改进了滚动条范围上的不足,可以得到实际的32位值

SetScrollInfo (hwnd, iBar, &si, bRedraw);
GetScrollInfo (hwnd, iBar, &si);

参数iBar 除了有SB_VERT 或 SB_HORZ。它也可以是SB_CTL,表示一个滚动条控件

typedef struct tagSCROLLINFO { 
    UINT cbSize; //设为sizeof(SCROLLINFO) 
    UINT fMask;   //要设置或获取的值
    int  nMin;  //范围的最小值
    int  nMax;  //范围的最大值
    UINT nPage;  //页面大小
    int  nPos;  //当前位置
    int  nTrackPos;  //当前追踪位置
}   SCROLLINFO, *LPSCROLLINFO; 
typedef SCROLLINFO CONST *LPCSCROLLINFO;

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

si.cbSize = sizeof(si); 或 si.cbSize = sizeof(SCROLLINFO);

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

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

在GetScrollInfo中指定了SIF_RANGE时,nMin 和nMax将返回当前的范围。

SIF_POS与:此类似;

SIF_PANG :可用于指定或获取页面大小。

在SetScrollInfo中你在nPage中指定页面大小,而GetScrollInfo在nPage中返回页面大小。如果不希望滑块大小发生变化,就不要使用这个标志。

自适应滑块大小垂直滚动条:

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
     static int  cxChar, cxCaps, cyChar, cyClient;
     HDC         hdc ;
     int         i, y ,iVertPos,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);
          return 0 ;

     case WM_SIZE:
          cyClient = HIWORD (lParam) ;
     si.fMask = SIF_RANGE|SIF_PAGE;
      si.cbSize = sizeof(si);
      si.nMin = 0;
      si.nMax = NUMLINES - 1;
      si.nPage = cyClient / cyChar;
  SetScrollInfo(hwnd,SB_VERT,&si,TRUE);
          return 0 ;

     case WM_VSCROLL:
    si.cbSize = sizeof(si);
    si.fMask = SIF_ALL;
    GetScrollInfo(hwnd,SB_VERT,&si);
    iVertPos = si.nPos;

          switch (LOWORD (wParam))
          {
      case SB_TOP:
        si.nPos = si.nMin;
        return 0;
      case SB_BOTTOM:
        si.nPos = si.nMax;
        return 0;
      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 ;
          }

          si.fMask = SIF_POS;
     SetScrollInfo(hwnd,SB_VERT,&si,TRUE);
     GetScrollInfo(hwnd,SB_VERT,&si);
     if(si.nPos != iVertPos)
     {
      ScrollWindow(hwnd,0,cyChar * (iVertPos - si.nPos),NULL,NULL);
      UpdateWindow(hwnd);
     }
          return 0 ;
     case WM_PAINT:
          hdc = BeginPaint (hwnd, &ps) ;
     
     si.cbSize = sizeof(si);
     si.fMask = SIF_POS;
     GetScrollInfo(hwnd,SB_VERT,&si);
     iVertPos = si.nPos;

     iPaintBeg = max(0,iVertPos + ps.rcPaint.top /cyChar);
     iPaintEnd = min(NUMLINES - 1,iVertPos + ps.rcPaint.bottom / cyChar);  //滑块位置相对 无效区域的大小???

          for (i = iPaintBeg ; i <= iPaintEnd ; i++)
          {
               y = cyChar * (i - iVertPos) ;
     
               TextOut (hdc, 0, y,
                        sysmetrics[i].szLabel,
                        lstrlen (sysmetrics[i].szLabel)) ;
     
               TextOut (hdc, 22 * cxCaps, y,
                        sysmetrics[i].szDesc,
                        lstrlen (sysmetrics[i].szDesc)) ;
     
               SetTextAlign (hdc, TA_RIGHT | TA_TOP) ;
     
               TextOut (hdc, 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) ;
}

运行结果:

这个版本的滚动条不用担心滚动的太多了。也不需要自己调整。但是还有一些细节没有弄明白...
理解以后再添加注释

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP