在上一篇中,我们使用老式的做法添加滚动条,他虽然运行良好,但是,效率不高.我们在win32之后有了新式的做法:
SetScrollInfo和GetScrollInfo已经可以完成前面的所有功能,并且新添加了两个特性:
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