[ windows程序设计(programing windows) ] - 窗口滚动条

    xiaoxiao2021-03-25  89

    本次要介绍的是窗口滚动条。滚动条一般在要显示的东西过多以至于屏幕无法完整呈现时使用。此时,你可以用它来滚动窗口,来显示内容的不同部分。

    滚动条这个东西用电脑应该都见得多了,由两个中间包含三角的方块和一个位于两个方块之间的长条组成,一般来说这个长条是可以随着显示内容占总内容的多少来改变长度的这个比例越小,长条越小。

    想要在窗口中添加滚动条十分的简单,只需要在CreateWindow函数的第三个参数dwStyle中加入WS_VSCROLL和WS_HSCROLL即可,当然你只添加其中一个也可以,H代表水平滚动条,V代表垂直滚动条。需要注意的是显示区域不包括滚动条占据的空间,对于特定的机器,水平滚动条的高度和垂直滚动条的宽度是恒定的,想要获取这些值,你可以用GetSystemMetrics函数来获得。

    滚动条有其范围和位置,范围由一对整数iMin和iMax表示,位置由一个整数iPos来表示。默认情况下nMin为0,nMax为100,当然这些你是可以用SetScrollInfo函数来更改的。

    接下来讲一下用滚动条的一般流程,

    首先你需要在WM_CREATE消息下处理一下基础信息,比如说你要滚动的内容是文字,那么你就需要计算出文字的宽,高以及大写时候的宽,这里需要用到一个结构体是TEXTMETRIC,这个结构体有很多成员,但我只将用到的以代码的形式呈现出来,

    TEXTMETRIC tm; GetTextMetrics(hdc, &tm); cxChar = tm.tmAveCharWidth; cxUpper = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2; cyChar = tm.tmHeight + tm.tmExternalLeading; cxChar是字符的宽度,cyChar是字符的高度,cxUpper是字符大写后的宽度。为什么大写的字符宽度要这么算,因为对于等宽字体来说,tm.tmPitchAndFamily的最低位是0,cxUpper等于cxChar,对于非等宽字体来说,tm.tmPitchAndFamily的最低位是1,cxUpper应该是cxChar的1.5倍,所以就会有上面的的代码写法。除了一些字符的信息,你还可以计算以信息比如说总共的文字行数等等。。。

    然后你需要在WM_SIZE消息下设置滚动条的信息。这里又要用到另一个结构体,叫SCROLLINFO

    typedef struct tagSCROLLINFO { UINT cbSize; UINT fMask; int nMin; int nMax; UINT nPage; int nPos; int nTrackPos; } SCROLLINFO, *LPCSCROLLINFO; cbSize是结构体的大小,sizeof(si);

    fMask指定你要设置或者获取的滚动条信息,

    SIF_ALLSIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOSSIF_DISABLENOSCROLL只有在设置信息的时候才有用,如果滚动条没有必要了,请设置此参数来禁用滚动条而不是删除滚动条SIF_PAGE指出nPage成员包含一个页面的大小,单位是像素。SIF_POS指出nPos成员包含滚动条的位置,该数值是从iMin到iMax中的一个值,用户拖拽滚动框的时候不会更新SIF_RANGE指出nMin和nMax成员包含滚动条范围的最小值和最大值SIF_TRACKPOS指出nTrackPos成员包含滚动条的当前位置,用户拖拽时会更新 比如说你要设置滚动条范围,那么你应该这么写

    cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); si.cbSize = sizeof(si); si.fMask = SIF_RANGE; si.nMin = 0; si.nMax = NUMLINES - 1; SetScrollInfo(hWnd, SB_VERT, &si, true); si.cbSize = sizeof(si); si.fMask = SIF_RANGE; si.nMin = 0; si.nMax = 2 + iMaxWidth / cxChar; SetScrollInfo(hWnd, SB_HORZ, &si, true);这里有一个新的函数,SetScrollInfo,这个的用法和GetScrollInfo差不多,只不过Get的版本比Set的版本少了最后一个参数fRedraw,这个这个参数为true则滚动条会重画来反应改变,false则不会重画。其余的参数我相信读者能看懂。 第三步你需要处理 WM_HSCROLL和 WM_VSCROLL消息,

    先来介绍wParam,LOWROD(wParam)指出了请求码,我将它们列在下面

    SB_BOTTOM滚动到最右端或者最下端,视滚动条类型而定SB_ENDSCROLL结束滚动SB_LINEDOWN向下滚动一行,实际上是将文字向上移SB_LINEUP向上滚动一行,实际上是将文字向下移SB_PAGEDOWN向下滚动一页,SB_PAGEUP向上滚动一页,SB_THUMBPOSITION用户拖拽动作完成后的位置SB_THUMBTRACK用户拖拽中的位置SB_TOP滚动到最左端或者最上端, 注意:当LOWROD(wParam)为SB_THUMBPOSITION或者SB_THUMBTRACK时,HIWORD(wParam)指出滚动条的位置。

    lParam,在标准滚动条下这个参数不使用,也就是我当前介绍的这种滚动条;还有一种是滚动条控件,我会在后面的文章介绍,这种情况下是滚动条控件的句柄。

    所以我们需要先用GetScrollInfo函数获取当前滚动条的状态信息,然后用switch语句来对不同的请求码做出不同的响应,最后再用SetScrollInfo函数来设置改变后的滚动条信息,然后我给出一段样例代码,

    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; 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; } 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), nullptr, nullptr); UpdateWindow(hWnd); } break;这段代码给出来的是处理垂直滚动条的,大体上和我之前的讲的差不了多少,但是我在这里对一些部分进行重点介绍,

    首先是为什么Set完了之后又有一个Get,并且还要判断改变之后的和当前的是否一样,由于windows的调整,滚动条的位置可能和我们设置的不一样,所以需要再获取一遍并且判断是否一样,如果不一样再滚动。其次,我在前面讲过,nPos应该是一个页面的像素数量,但是这个只是一般的用法,在我上面给出的代码中,nPos实际为cxClient / cxChar,这样子的话nPage实际是一页可以显示的完整的行的数量,设置为cxClient也是可以的。

    这里我再介绍一下ScrollWindow这个函数,函数原型如下

    BOOL ScrollWindow( _In_ HWND hWnd, _In_ int XAmount, _In_ int YAmount, _In_ const RECT *lpRect, _In_ const RECT *lpClipRect );第一个是窗口句柄;第二个是沿着x轴滚动的数量,以像素为单位;第三个是沿着y轴滚动的数量,以像素为单位;第四个是将要被滚动的矩形的指针,如果为nullptr则整个用户区域都要被重画;第五个是上一个矩形的一部分,从外部滚动到内部的点会被重画,而从内部滚动到外部的点不会被重画。

    注:现在这个函数只是为了提供向后的兼容性而不应该被使用,如果想要使用应该使用ScrollWindowEx函数,这个函数我再此不做介绍,有兴趣的可以google一下。

    最后一步就是处理WM_PAINT消息,这一步你需要显示未被显示的东西。这句话是什么意思呢,ScrollWindow函数实际上做了对滚动区域的像素的移动,但是滚动区域外本来没有像素,那么再滚动完就会有空白出现,此时你就需要填充空白区域。

    到目前位置一个基本的滚动条就可以用了。

    但是一般来说,程序都是要对一些按键响应的,比如对于滚动条来说,你需要对Home,End,PageUp,PageDown等键做出反应,窗口滚动条不需要考虑这些东西,而后面介绍的滚动条控件则需要考虑。

    以上便是本文的全部内容,欢迎指出错误,互相学习。

    转载请注明原文地址: https://ju.6miu.com/read-16636.html

    最新回复(0)