windows编程之滚动条(新式滚动条函数)

在上一篇中,我们使用老式的做法添加滚动条,他虽然运行良好,但是,效率不高.我们在win32之后有了新式的做法:

SetScrollInfoGetScrollInfo已经可以完成前面的所有功能,并且新添加了两个特性:

  1.SetScrollInfo:第一个功能涉及卷动方块的大小。卷动方块大小在上一个博客中的程序中是固定的。然而,在您可能使用到的一些Windows应用程序中,卷动方块大小与在窗口中显示的文件大小成比例。显示的大小称作「页面大小」。算法为:

    卷动方块大小/滚动长度=页面大小/范围=显示文件数量/文件总大小.

  可以使用SetScrollInfo来设置页面大小(从而设置了卷动方块的大小),

  2.GetScrollInfo:增加了第二个重要的功能,或者说它改进了目前API的不足。假设您要使用65,536或更大单位的范围,这在16位Windows中是不可能的。当然在Win32中,函数被定义为可接受32位参数,因此是没有问题的。(记住如果使用这样大的范围,卷动方块的实际物理位置数仍然由卷动列的图素大小限制)。然而,当使用SB_THUMBTRACK或SB_THUMBPOSITION通知码得到WM_VSCROLL或WM_HSCROLL消息时,只提供了16位数据来指出卷动方块的目前位置。通过GetScrollInfo函数可以取得真实的32位值。

语法为:

SetScrollInfo (hwnd, iBar, &si, bRedraw) ;
        
GetScrollInfo (hwnd, iBar, &si) ;
/*
两个函数的第三个参数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, * PSCROLLINFO ;
/*
在使用的时候,需要:
*/
SCROLLINFO si ;
si.cbSize = sizeof (si) ;

 fMask是一个flag的作用:

  1.SetScrollInfo函数使用SIF_RANGE旗标时,必须把nMin和nMax字段设定为所需的滚动条范围。GetScrollInfo函数使用SIF_RANGE旗标时,应把nMin和nMax字段设定为从函数传回的目前范围。

  2.SIF_POS旗标也一样。当通过SetScrollInfo使用它时,必须把结构的nPos字段设定为所需的位置。可以通过GetScrollInfo使用SIF_POS旗标来取得目前位置。

  3.使用SIF_PAGE旗标能够取得页面大小。用SetScrollInfo函数把nPage设定为所需的页面大小。GetScrollInfo使用SIF_PAGE旗标可以取得目前页面的大小。如果不想得到比例化的滚动条,就不要使用该旗标。

  4.当处理带有SB_THUMBTRACK或SB_THUMBPOSITION通知码的WM_VSCROLL或WM_HSCROLL消息时,通过GetScrollInfo只使用SIF_TRACKPOS旗标。从函数的传回中,SCROLLINFO结构的nTrackPos字段将指出目前的32位的卷动方块位置。

  5.在SetScrollInfo函数中仅使用SIF_DISABLENOSCROLL旗标。如果指定了此旗标,而且新的滚动条参数使滚动条消失,则该滚动条就不能使用了(下面会有更多的解释)。

  6.SIF_ALL旗标是SIF_RANGE、SIF_POS、SIF_PAGE和SIF_TRACKPOS的组合。在WM_SIZE消息处理期间设置滚动条参数时,这是很方便的(在SetScrollInfo函数中指定SIF_TRACKPOS后,它会被忽略)。这在处理滚动条消息时也是很方便的。

在上一篇文章的代码中,我们发现设定滚动条范围的时候,我们拉到底,会出现最后一行在一页的最上面的情况,其实我们只需要将最后一行放在底部就行了.所以,我们应该这样子做》

iVscrollMax = max (0, NUMLINES - cyClient / cyChar) ;
        
SetScrollRange (hwnd, SB_VERT, 0, iVscrollMax, TRUE) ;

但是,在新的滚动条函数在,无需这样子设置,他已经帮你处理好了这个问题.于是:si可以大胆的设置为NUMLINES-1;

可以向下面这样设置SCROLLINFO这个结构体:

si.cbSize   = sizeof (SCROLLINFO) ;
        
si.cbMask   = SIF_RANGE | SIF_PAGE ;
        
si.nMin     = 0 ;
        
si.nMax     = NUMLINES - 1 ;
        
si.nPage    = cyClient / cyChar ;
        
SetScrollInfo (hwnd, SB_VERT, &si, TRUE) ;
        

综上所述:我们应该这样子去用新的函数做一个滚动条:

一下全都是在窗口函数中(CALLBACK)

1.设定变量: 

static int  cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth ;
/*
cxChar-字体平均宽度
cxcaps-大写字体平均宽度
cychar-字体平均高度
cyclient,cxclient-显示区高度和宽度
iMaxwidth-显示区最大宽度
*/
        
    HDC    hdc ;  //设备句柄
        
    int    i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd ;
/*
i-迭代
x-显示窗口横坐标
y-显示窗口纵坐标
ivertpos-垂直滚动条位置
ihorzpos-水平滚动条位置
ipaintbeg-重绘区开始坐标
ipaintend-重绘区结束坐标
*/        
    PAINTSTRUCT ps ;//绘图结构体
        
    SCROLLINFO  si ;//滚动条结构体
        
    TCHAR       szBuffer[10] ; //缓冲区
        
    TEXTMETRIC  tm ;//字体结构体

2.在WM_CREARTE中:

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 ;

3.在WM_SIZE中:

case WM_SIZE:
        
    cxClient = LOWORD (lParam) ; //获取显示区宽度
        
    cyClient = HIWORD (lParam) ;//获取显示区高度
    
    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) ;
    //之前在初始化垂直滚动条的信息。
    
    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 ;

4.在WM_VSCROLL中(横向也是如此处理,在此处不贴代码了):

 si.cbSize     = sizeof (si) ;
        
    si.fMask      = SIF_ALL ;
        
    GetScrollInfo (hwnd, SB_VERT, &si) ;
        
            // Save the position for comparison later on
        
    iVertPos = si.nPos ;
    //获取当前垂直滚动条的信息,初始化ivertpos
        
    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) ;//强制重绘,发送wm_paint
        
    }
        
    return 0 ;
        

5.在WM_PAINT中:

 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) ;//获取在WM_VSCROLL中更新的信息。
        
    iVertPos = si.nPos ;
        

            // Get horizontal scroll bar position
        
    GetScrollInfo (hwnd, SB_HORZ, &si) ;//获取在WM_HSCROLL中更新的信息
        
    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) ;
        
    }

注1:这些代码无法粘贴之后直接运行,因为少了一部分代码,包括一个头文件和WIN_MAIN函数.完整代码在github上面:

注2:此代码在VS2012下完成.

完整代码地址:https://github.com/shangbo/windows_api/tree/master/scroll2

原文地址:https://www.cnblogs.com/SoulReaper/p/3316234.html