窗口滚动条的显示
用于创建窗口的 CreateWindow 函数的第三个参数可以设置 WS_HSCROLL(水平滚动条) 和 WS_VSCROLL(垂直滚动条) 这两个风格标识符,以标识窗口附带水平滚动条和(或)垂直滚动条
滚动条消息
当用户单击或拖动窗口滚动条时,Windows 向窗口过程发送 WM_HSCROLL(水平滚动) 消息或 WM_VSCROLL(垂直滚动) 消息,鼠标按下与鼠标松开都会产生这些消息(即一个动作至少会产生两条消息)。消息伴随着消息参数,lParam 的值为滚动条的句柄(当是窗口的滚动条时,此值为 NULL 可以忽略),wParam 的低位字代表鼠标在滚动条上的动作:
向上(左)滚动一行:SB_LINEUP
向下(右)滚动一行:SB_LINEDOWN
向上(左)滚动一页:SB_PAGEUP
向下(右)滚动一页:SB_PAGEDOWN
拖动滚动条不放:SB_THUMBTRACK(一直发送),wParam 的高位字是用户拖动滑块的当前位置
拖动滚动条时释放鼠标:SB_THUMBPOSITION,wParam 的高位字是松开鼠标键时,滑块的最终位置
松开鼠标键时:SB_ENDSCROLL
滚动条的范围和位置
调用 SetScrollRange 函数可以设置滚动条的范围:
BOOL SetScrollRange( HWND hWnd, // 窗口的句柄 int nBar, // 滚动条类型(SB_VERT 或 SB_HORZ) int nMinPos, // 最小位置 int nMaxPos, // 最大位置 BOOL bRedraw // 是否需要 Windows 根据新的范围来重绘滚动条(若编程时调用其它函数来调整滚动条的显示,则最好设为FALSE,避免过多重绘) );
调用 SetScrollPos 函数可以调整滚动条的位置:
int SetScrollPos( HWND hWnd, // 窗口的句柄 int nBar, // 滚动条类型(SB_VERT 或 SB_HORZ) int nPos, // 要设置的滚动条的位置(必须在最小位置和最大位置之间) BOOL bRedraw // 是否需要 Windows 根据新的范围来重绘滚动条(若编程时调用其它函数来调整滚动条的显示,则最好设为FALSE,避免过多重绘) );
调用 GetScrollRange 函数可以获取滚动条的范围:
BOOL GetScrollRange( HWND hWnd, int nBar, LPINT lpMinPos, // 最小位置 LPINT lpMaxPos // 最大位置 );
调用 GetScrollPos函数可以获取滚动条的位置:
int GetScrollPos( HWND hWnd, int nBar );
滚动条编程要点
Windows 负责的部分:
- 处理所有鼠标消息
- 单击滚动条时,提供反向显示的闪烁
- 拖动滑块时,显示滑块在滚动条内移动
- 向窗口过程发送滚动条消息
程序负责的部分:
- 初始化滚动条的范围和位置
- 处理窗口过程的滚动条消息
- 更新滑块的位置
- 根据滚动条变化更新客户区的内容
滚动条的改良
- SCROLLINFO 结构用于存放滚动条信息,其定义如下:
typedef struct tagSCROLLINFO { UINT cbSize; // 设为 sizeof (SCROLLINFO) UINT fMask; // 要设置和获取的值 int nMin; // 范围的最小值 int nMax; // 范围的最大值 UINT nPage; // 页面大小 int nPos; // 当前位置 int nTrackPos; // 当前追踪位置 } SCROLLINFO, *LPSCROLLINFO; typedef SCROLLINFO CONST *LPCSCROLLINFO;
cbSize 字段表示该结构的大小,通常被填充为 sizeof(SCROLLINFO) ,这样方便新版本的 Windows 扩充字段。
fMask 字段把 SIF 为前缀的标志用位或组合起来,表示设置和获取的值。
SIF_RANGE:该标志表示滚动条范围,此时 nMin、nMax 有效
SIF_POS:该标志表示滚动条位置,此时 nPos 有效
SIF_PAGE:该标志表示页面大小,此时 nPage 有效
SIF_TRACKPOS:该标志表示当前滑块的位置(只用于 GetScrollInfo 中,只在 SB_THUMBTRACK 和 SB_THUMBPOSITION 有效),nTrackPos 有效
SIF_DISABLENOSCROLL:该标志表示当滚动条不显示时,显示禁用的滚动条样式
SIF_ALL:该标志是 SIF_RANGE、SIF_POS、SIF_PAGE、SIF_TRACKPOS 的组合
- SetScrollInfo 函数用于设置滚动条信息:
int SetScrollInfo( HWND hwnd, // 当前窗口句柄 int fnBar, // 滚动条类型(SB_VERT 或者 SB_HORZ) LPCSCROLLINFO lpsi, // SCROLLINFO 结构的地址 BOOL fRedraw // 是否重绘的标志 );
- GetScrollInfo 函数用于获取滚动条信息:
BOOL GetScrollInfo( HWND hwnd, // 当前窗口句柄 int fnBar, // 滚动条类型(SB_VERT 或者 SB_HORZ) LPCSCROLLINFO lpsi, // SCROLLINFO 结构的地址 );
- ScrollWindow 函数用于滚动窗口客户区内容:
BOOL ScrollWindow( HWND hWnd, // 当前窗口句柄 int XAmount, // 水平滚动量,为负则向左滚动 int YAmount, // 垂直滚动量,为负则向上滚动 const RECT *lpRect, // 指定滚动客户区矩形的范围 const RECT *lpClipRect // 指定滚动的裁剪区域 );
最终版 SYSMET 程序示例
- SYSMET.h 源代码:
/*----------------------------------------------- SYSMETS.H -- System metrics display structure -----------------------------------------------*/ #define NUMLINES ((int) (sizeof sysmetrics / sizeof sysmetrics [0])) struct { int iIndex ; TCHAR * szLabel ; TCHAR * szDesc ; } sysmetrics [] = { SM_CXSCREEN, TEXT ("SM_CXSCREEN"), TEXT ("Screen width in pixels"), SM_CYSCREEN, TEXT ("SM_CYSCREEN"), TEXT ("Screen height in pixels"), SM_CXVSCROLL, TEXT ("SM_CXVSCROLL"), TEXT ("Vertical scroll width"), SM_CYHSCROLL, TEXT ("SM_CYHSCROLL"), TEXT ("Horizontal scroll height"), SM_CYCAPTION, TEXT ("SM_CYCAPTION"), TEXT ("Caption bar height"), SM_CXBORDER, TEXT ("SM_CXBORDER"), TEXT ("Window border width"), SM_CYBORDER, TEXT ("SM_CYBORDER"), TEXT ("Window border height"), SM_CXFIXEDFRAME, TEXT ("SM_CXFIXEDFRAME"), TEXT ("Dialog window frame width"), SM_CYFIXEDFRAME, TEXT ("SM_CYFIXEDFRAME"), TEXT ("Dialog window frame height"), SM_CYVTHUMB, TEXT ("SM_CYVTHUMB"), TEXT ("Vertical scroll thumb height"), SM_CXHTHUMB, TEXT ("SM_CXHTHUMB"), TEXT ("Horizontal scroll thumb width"), SM_CXICON, TEXT ("SM_CXICON"), TEXT ("Icon width"), SM_CYICON, TEXT ("SM_CYICON"), TEXT ("Icon height"), SM_CXCURSOR, TEXT ("SM_CXCURSOR"), TEXT ("Cursor width"), SM_CYCURSOR, TEXT ("SM_CYCURSOR"), TEXT ("Cursor height"), SM_CYMENU, TEXT ("SM_CYMENU"), TEXT ("Menu bar height"), SM_CXFULLSCREEN, TEXT ("SM_CXFULLSCREEN"), TEXT ("Full screen client area width"), SM_CYFULLSCREEN, TEXT ("SM_CYFULLSCREEN"), TEXT ("Full screen client area height"), SM_CYKANJIWINDOW, TEXT ("SM_CYKANJIWINDOW"), TEXT ("Kanji window height"), SM_MOUSEPRESENT, TEXT ("SM_MOUSEPRESENT"), TEXT ("Mouse present flag"), SM_CYVSCROLL, TEXT ("SM_CYVSCROLL"), TEXT ("Vertical scroll arrow height"), SM_CXHSCROLL, TEXT ("SM_CXHSCROLL"), TEXT ("Horizontal scroll arrow width"), SM_DEBUG, TEXT ("SM_DEBUG"), TEXT ("Debug version flag"), SM_SWAPBUTTON, TEXT ("SM_SWAPBUTTON"), TEXT ("Mouse buttons swapped flag"), SM_CXMIN, TEXT ("SM_CXMIN"), TEXT ("Minimum window width"), SM_CYMIN, TEXT ("SM_CYMIN"), TEXT ("Minimum window height"), SM_CXSIZE, TEXT ("SM_CXSIZE"), TEXT ("Min/Max/Close button width"), SM_CYSIZE, TEXT ("SM_CYSIZE"), TEXT ("Min/Max/Close button height"), SM_CXSIZEFRAME, TEXT ("SM_CXSIZEFRAME"), TEXT ("Window sizing frame width"), SM_CYSIZEFRAME, TEXT ("SM_CYSIZEFRAME"), TEXT ("Window sizing frame height"), SM_CXMINTRACK, TEXT ("SM_CXMINTRACK"), TEXT ("Minimum window tracking width"), SM_CYMINTRACK, TEXT ("SM_CYMINTRACK"), TEXT ("Minimum window tracking height"), SM_CXDOUBLECLK, TEXT ("SM_CXDOUBLECLK"), TEXT ("Double click x tolerance"), SM_CYDOUBLECLK, TEXT ("SM_CYDOUBLECLK"), TEXT ("Double click y tolerance"), SM_CXICONSPACING, TEXT ("SM_CXICONSPACING"), TEXT ("Horizontal icon spacing"), SM_CYICONSPACING, TEXT ("SM_CYICONSPACING"), TEXT ("Vertical icon spacing"), SM_MENUDROPALIGNMENT, TEXT ("SM_MENUDROPALIGNMENT"), TEXT ("Left or right menu drop"), SM_PENWINDOWS, TEXT ("SM_PENWINDOWS"), TEXT ("Pen extensions installed"), SM_DBCSENABLED, TEXT ("SM_DBCSENABLED"), TEXT ("Double-Byte Char Set enabled"), SM_CMOUSEBUTTONS, TEXT ("SM_CMOUSEBUTTONS"), TEXT ("Number of mouse buttons"), SM_SECURE, TEXT ("SM_SECURE"), TEXT ("Security present flag"), SM_CXEDGE, TEXT ("SM_CXEDGE"), TEXT ("3-D border width"), SM_CYEDGE, TEXT ("SM_CYEDGE"), TEXT ("3-D border height"), SM_CXMINSPACING, TEXT ("SM_CXMINSPACING"), TEXT ("Minimized window spacing width"), SM_CYMINSPACING, TEXT ("SM_CYMINSPACING"), TEXT ("Minimized window spacing height"), SM_CXSMICON, TEXT ("SM_CXSMICON"), TEXT ("Small icon width"), SM_CYSMICON, TEXT ("SM_CYSMICON"), TEXT ("Small icon height"), SM_CYSMCAPTION, TEXT ("SM_CYSMCAPTION"), TEXT ("Small caption height"), SM_CXSMSIZE, TEXT ("SM_CXSMSIZE"), TEXT ("Small caption button width"), SM_CYSMSIZE, TEXT ("SM_CYSMSIZE"), TEXT ("Small caption button height"), SM_CXMENUSIZE, TEXT ("SM_CXMENUSIZE"), TEXT ("Menu bar button width"), SM_CYMENUSIZE, TEXT ("SM_CYMENUSIZE"), TEXT ("Menu bar button height"), SM_ARRANGE, TEXT ("SM_ARRANGE"), TEXT ("How minimized windows arranged"), SM_CXMINIMIZED, TEXT ("SM_CXMINIMIZED"), TEXT ("Minimized window width"), SM_CYMINIMIZED, TEXT ("SM_CYMINIMIZED"), TEXT ("Minimized window height"), SM_CXMAXTRACK, TEXT ("SM_CXMAXTRACK"), TEXT ("Maximum dragable width"), SM_CYMAXTRACK, TEXT ("SM_CYMAXTRACK"), TEXT ("Maximum dragable height"), SM_CXMAXIMIZED, TEXT ("SM_CXMAXIMIZED"), TEXT ("Width of maximized window"), SM_CYMAXIMIZED, TEXT ("SM_CYMAXIMIZED"), TEXT ("Height of maximized window"), SM_NETWORK, TEXT ("SM_NETWORK"), TEXT ("Network present flag"), SM_CLEANBOOT, TEXT ("SM_CLEANBOOT"), TEXT ("How system was booted"), SM_CXDRAG, TEXT ("SM_CXDRAG"), TEXT ("Avoid drag x tolerance"), SM_CYDRAG, TEXT ("SM_CYDRAG"), TEXT ("Avoid drag y tolerance"), SM_SHOWSOUNDS, TEXT ("SM_SHOWSOUNDS"), TEXT ("Present sounds visually"), SM_CXMENUCHECK, TEXT ("SM_CXMENUCHECK"), TEXT ("Menu check-mark width"), SM_CYMENUCHECK, TEXT ("SM_CYMENUCHECK"), TEXT ("Menu check-mark height"), SM_SLOWMACHINE, TEXT ("SM_SLOWMACHINE"), TEXT ("Slow processor flag"), SM_MIDEASTENABLED, TEXT ("SM_MIDEASTENABLED"), TEXT ("Hebrew and Arabic enabled flag"), SM_MOUSEWHEELPRESENT, TEXT ("SM_MOUSEWHEELPRESENT"), TEXT ("Mouse wheel present flag"), SM_XVIRTUALSCREEN, TEXT ("SM_XVIRTUALSCREEN"), TEXT ("Virtual screen x origin"), SM_YVIRTUALSCREEN, TEXT ("SM_YVIRTUALSCREEN"), TEXT ("Virtual screen y origin"), SM_CXVIRTUALSCREEN, TEXT ("SM_CXVIRTUALSCREEN"), TEXT ("Virtual screen width"), SM_CYVIRTUALSCREEN, TEXT ("SM_CYVIRTUALSCREEN"), TEXT ("Virtual screen height"), SM_CMONITORS, TEXT ("SM_CMONITORS"), TEXT ("Number of monitors"), SM_SAMEDISPLAYFORMAT, TEXT ("SM_SAMEDISPLAYFORMAT"), TEXT ("Same color format flag") } ;
- SYSMET.c 源代码:
#include <windows.h> #include <strsafe.h> #include "SysMets.h" LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; TEXTMETRIC tm; static int cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth; int iVertPos, iHorzPos, iBeginLine, iEndLine, i, x, y; size_t nLength; SCROLLINFO si; TCHAR szBuffer[100]; switch (message) { 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; iMaxWidth = cxCaps * 22 + cxChar * 40; ReleaseDC(hwnd, hdc); return 0; case WM_SIZE: cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); si.cbSize = sizeof(si); si.fMask = SIF_RANGE | SIF_PAGE | SIF_DISABLENOSCROLL; 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 | SIF_DISABLENOSCROLL; si.nMin = 0; si.nMax = iMaxWidth / cxChar + 5; si.nPage = cxClient / cxChar; SetScrollInfo(hwnd, SB_HORZ, &si, TRUE); return 0; case WM_VSCROLL: si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hwnd, SB_VERT, &si); iVertPos = si.nPos; switch (LOWORD(wParam)) { 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; } si.cbSize = sizeof(si); 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), NULL, NULL); } return 0; case WM_HSCROLL: si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hwnd, SB_HORZ, &si); iHorzPos = si.nPos; switch (LOWORD(wParam)) { case SB_LINELEFT: si.nPos -= 1; break; case SB_LINERIGHT: si.nPos += 1; break; case SB_PAGELEFT: si.nPos -= si.nPage; break; case SB_PAGERIGHT: si.nPos += si.nPage; break; case SB_THUMBTRACK: si.nPos = si.nTrackPos; break; } si.cbSize = sizeof(si); si.fMask = SIF_POS; SetScrollInfo(hwnd, SB_HORZ, &si, TRUE); GetScrollInfo(hwnd, SB_HORZ, &si); if (si.nPos != iHorzPos) { ScrollWindow(hwnd, cxChar * (iHorzPos - si.nPos), 0, NULL, NULL); } return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hwnd, SB_VERT, &si); iVertPos = si.nPos; GetScrollInfo(hwnd, SB_HORZ, &si); iHorzPos = si.nPos; // Control the line range iBeginLine = max(0, iVertPos + (ps.rcPaint.top / cyChar)); iEndLine = min(NUMLINES - 1, iVertPos + (ps.rcPaint.bottom / cyChar)); for (i = iBeginLine; i <= iEndLine; i++) { x = cxChar * (1 - iHorzPos); y = cyChar * (i - iVertPos); StringCchPrintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), sysmetrics[i].szLabel); StringCchLength(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), &nLength); TextOut(hdc, x, y, szBuffer, nLength); StringCchPrintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), sysmetrics[i].szDesc); StringCchLength(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), &nLength); TextOut(hdc, x + cxCaps * 22, y, szBuffer, nLength); StringCchPrintf(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), TEXT("%5d"), sysmetrics[i].iIndex); StringCchLength(szBuffer, sizeof(szBuffer) / sizeof(TCHAR), &nLength); TextOut(hdc, x + cxCaps * 22 + cxChar * 40, y, szBuffer, nLength); } EndPaint(hwnd, &ps); return 0; case WM_CLOSE: if (MessageBox(hwnd, TEXT("Do you really want to quit?"), TEXT("Please confirm:"), MB_ICONQUESTION | MB_OKCANCEL) == IDOK) { DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { LPCTSTR lpszClassName = TEXT("ScrollDemo"); LPCTSTR lpszWindowName = TEXT("Scroll Demo"); WNDCLASS wndclass; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); wndclass.hInstance = hInstance; wndclass.lpfnWndProc = WndProc; wndclass.lpszClassName = lpszClassName; wndclass.lpszMenuName = NULL; wndclass.style = CS_HREDRAW | CS_VREDRAW; if (!RegisterClass(&wndclass)) { MessageBox(NULL, TEXT("This program requires Windows NT!"), lpszWindowName, MB_ICONERROR); return 0; } HWND hwnd = CreateWindow( lpszClassName, lpszWindowName, WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
滚动条小结
总的来说,SYSMET 程序可以细分为如下结构:
对 WM_CREATE 的消息的响应,获取系统字符的各个参数。
对 WM_SIZE 消息的响应,改变水平滚动条和垂直滚动条的范围(nMin、nMax)和页面大小(nPage)
对 WM_VSCROLL 消息的响应,根据 LOWORD(wParam) 改变垂直滚动条滑块的位置(nPos),并根据位置变化的偏移量,滚动客户区(ScrollWindow)
对 WM_HSCROLL 消息的响应,根据 LOWORD(wParam) 改变水平滚动条滑块的位置(nPos),并根据位置变化的偏移量,滚动客户区(ScrollWindow)
对 WM_PAINT 消息的响应,根据滚动条滑块位置和 PAINTSTRUCT 的 rcPaint 无效矩形区域信息计算出,文本绘制的开始行(iBeginLine)和结束行(iEndLine);并根据滚动条滑块位置,计算出文本输出的起始水平位置(x)和垂直位置(y),并输出文本(TextOut)