• 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

  • 相关阅读:
    每天拿出来2小时浪费(文/王路) 作者: 王路
    objective-c自学总结(二)---init/set/get方法
    objective-c自学总结(一)---面向对象
    水仙花数
    独木舟上的旅行
    阶乘之和
    小明的调查统计
    管闲事的小明
    重温《STL源码剖析》笔记 第一章
    重温《STL源码剖析》笔记 第三章
  • 原文地址:https://www.cnblogs.com/SoulReaper/p/3316234.html
Copyright © 2020-2023  润新知