第三周:WindowsAPI编程:滚动条

一、滚动条收到的消息:

在用鼠标单击滚动条或者拖动卷动方块时,Windows给窗口消息处理程序发送WM_VSCROLL(供上下移动)和WM_HSCROLL(供左右移动)消息。在滚动条上的每个鼠标动作都至少产生两个消息,一条在按下鼠标按钮时产生,一条在释放按钮时产生。拖动滚动条会持续发送滚动消息。
该消息的wparam的低16位为一个“通知码”,该码为定义的下列值之一,用于区别对滚动条的各种操作。

#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

lparam可以忽略,其只用于作为子窗口出现的滚动条。
处理 SB_THUMBPOSITION时,wParam的高字组是使用者释放鼠标键后卷动方块的最终位置。对于其它的卷动列操作,wParam的高字组应该被忽略。
程序能够处理SB_THUMBTRACK或SB_THUMBPOSITION消息,但一般不同时处理两者。如果处理SB_THUMBTRACK消息,在使用者拖动卷动方块时您需要移动显示区域的内容。而如果处理SB_THUMBPOSITION消息,则只需在使用者停止拖动卷动方块时移动显示区域的内容。处理SB_THUMBTRACK消息更好一些(但更困难),对于某些型态的数据,您的程序可能很难跟上产生的消息。

二、程序编写者需要做的事情:

在程序内使用滚动条时,程序写作者与Windows共同负责维护滚动条以及更新卷动方块的位置。下面是Windows对滚动条的处理:

  • 处理所有滚动条鼠标事件
  • 当使用者在滚动条内单击鼠标时,提供一种「反相显示」的闪烁
  • 当使用者在滚动条内拖动滚动方块时,移动滚动方块
  • 为包含滚动条窗口的窗口消息处理程序发送滚动条消息

以下是程序写作者应该完成的工作:

  • 初始化滚动条的范围和位置
  • 处理窗口消息处理程序的滚动条消息
  • 更新滚动条内滚动方块的位置
  • 更改显示区域的内容以响应对滚动条的更改

也就是说,程序需要初始化滚动条,处理消息,并在处理消息的过程中手动更新滚动方块位置、更新显示区域的内容。
具体来说,

  1. 根据通知码进行不同的操作
  2. 设置滑块位置等值
  3. 重新绘制界面

三、操作滚动条的方式(一)

在一个Windows窗口中,实现滚动条有两种方式。下面分别说说这两种方式。
第一种,使用函数:

int SetScrollPos(
  HWND hWnd,
  int  nBar,
  int  nPos,
  BOOL bRedraw );

BOOL SetScrollRange(
  HWND hWnd,
  int  nBar,
  int  nMinPos,
  int  nMaxPos,
  BOOL bRedraw );
  
//其它类似函数:
BOOL GetScrollRange(
  HWND  hWnd,
  int   nBar,
  LPINT lpMinPos,
  LPINT lpMaxPos );

int GetScrollPos(
  HWND hWnd,
  int  nBar );

这种方式主要在一些老程序中使用,现在的程序主要使用第二种方式。
此处以WM_VSCROLL为例,列出了处理消息的代码:

//定义的变量:
DOWRD wparam; 
static int iVscrollPos; //滚动方块位置
static int cyClient;    //窗口高
static int cyChar;      //一行文字占用的高度
const int NUMLINES=47;  //一个常数,代表总行数
HANDLE hwnd;            //窗口句柄

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

该示例代码展示了如何对消息进行操作。可以看到,程序保存了一个滚动方块位置的变量 iVscrollPos,并通过考虑窗口高度与行宽来实现翻页操作。

四、操作滚动条的方式(二)

其实这种方法主要的特点就是能够设置滑动方块的长度,使之适应屏幕与页面的比例。在处理消息方面,二者并未相差太大,不同主要在于使用的函数。
使用函数:

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

GetScrollInfo (hwnd, iBar, &si) ;

BOOL ScrollWindow(
  HWND       hWnd,
  int        XAmount,
  int        YAmount,
  const RECT *lpRect,
  const RECT *lpClipRect
);

参数中的 si 表示 SCROLLINFO 结构,定义为:

typedef struct tagSCROLLINFO
{
    UINT cbSize ;// set to sizeof (SCROLLINFO)
    UINT fMask ;  // values to set or get,一个flag
    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 ;

这两个函数根据 si 中的fMask所设置的值,在 si 中返回一定的数据或设置特定的值。这个特性,,,很奇怪,在python里从来没有遇到过。
在呼叫SetScrollInfo或GetScrollInfo之前,必须将cbSize字段设定为结构的大小:

si.cbSize = sizeof (si) ;

fMask字段可设置为下列值:

  • SIF_ALL
    SIF_PAGE、SIF_POS、SIF_RANGE 和 SIF_TRACKPOS 的组合。
  • SIF_DISABLENOSCROLL
    此值仅在设置滚动条的参数时使用。 如果滚动条的新参数使滚动条变得不必要,请禁用滚动条而不是将其移除。
  • SIF_PAGE
    nPage 成员包含比例滚动条的页面大小。
  • SIF_POS
    nPos 成员包含滚动框位置,在用户拖动滚动框时不会更新
  • SIF_RANGE
    nMin 和 nMax 成员包含滚动范围的最小值和最大值
  • SIF_TRACKPOS
    nTrackPos 成员包含用户拖动滚动框时的当前位置。

一般在获取scrollbar状态时使用SIF_All是很方便的选择,而设置scrollbar状态时应按需设置。
设置SIF_PAGE值可以改变滑块长度,滑块长度/ 滚动条长度 = RANGE / PAGE

上代码:

//定义的变量
SCROLLINFO si;
static int iVertPos;
HANDLE hwnd;
DWORD wParam;
static int cyChar;

case WM_VSCROLL:
            // Get all the vertical 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 ;

后语:写文章还是写的好慢。但是这逼着我熟悉这个知识点。可是会不会导致我学不完?肯定会。下次,搞个概览就好。毕竟一切都是为了逆向。不过,写这种文章真的能够提高我的表达能力么?还是说,我应该去写些垃圾小说?(写小说,就不可能发出来了,太差了)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容