就像玩War3时对不死族的恐惧一样,做界面时一直都不想去涉及滚动条,借端午节狠下心来把滚动条又看了一遍,再一次印证实践才是真理,把对滚动条的理解写下来,以便于以后查阅。
.gif)
上图是MSDN中对滚动条的描述,很直观,而滚动条相关的消息也不多最常用的也就是WM_VSCROLL和WM_HSCROLL,这两个消息的LOWORD (wParam)参数对应了一系列Request Code:
Request | Action | Response |
SB_LINEUP | The user clicks the top scroll arrow. | Decrements the scroll box position; scrolls toward the top of the data by one unit. |
SB_LINEDOWN | The user clicks the bottom scroll arrow. | Increments the scroll box position; scrolls toward the bottom of the data by one unit. |
SB_LINELEFT | The user clicks the left scroll arrow. | Decrements the scroll box position; scrolls toward the left end of the data by one unit. |
SB_LINERIGHT | The user clicks the right scroll arrow. | Increments the scroll box position; scrolls toward the right end of the data by one unit. |
SB_PAGEUP | The user clicks the scroll bar shaft above the scroll box. | Decrements the scroll box position by the number of data units in the window; scrolls toward the top of the data by the same number of units. |
SB_PAGEDOWN | The user clicks the scroll bar shaft below the scroll box. | Increments the scroll box position by the number of data units in the window; scrolls toward the bottom of the data by the same number of units. |
SB_PAGELEFT | The user clicks the scroll bar shaft to the left of the scroll box. | Decrements the scroll box position by the number of data units in the window; scrolls toward the left end of the data by the same number of units. |
SB_PAGERIGHT | The user clicks the scroll bar shaft to the right of the scroll box. | Increments the scroll box position by the number of data units in the window; scrolls toward the right end of the data by the same number of units. |
SB_THUMBPOSITION | The user releases the scroll box after dragging it. | Sets the scroll box to the position specified in the message; scrolls the data by the same number of units the scroll box has moved. |
SB_THUMBTRACK | The user drags the scroll box. | Sets the scroll box to the position specified in the message and scrolls the data by the same number of units the scroll box has moved for applications that draw data quickly. Applications that cannot draw data quickly must wait for the SB_THUMBPOSITION request code before moving the scroll box and scrolling the data. |
SB_ENDSCROLL | The user releases the mouse after holding it on an arrow or in the scroll bar shaft. | No response is needed. |
1**设置滚动条参数**
一般都是在WM_SIZE消息中设置滚动条的信息,这样当窗口大小变化时滚动条可以做相应的改变,以显示或者隐藏之或者改变其ScrollBox的大小。
MSDN上面有一段说道滚动条的显示问题:
Scroll Bar Visibility
The system hides and disables a standard scroll bar when equal minimum and maximum values are specified. The system also hides and disables a standard scroll bar if you specify a page size that includes the entire scroll range of the scroll bar. This is the way to temporarily hide a scroll bar when it is not needed for the content of the client area. There is no need to make scrolling requests through the scroll bar when it is hidden. The system enables the scroll bar and shows it again when you set the minimum and maximum values to unequal values or when the page size that does not include the entire scroll range. The ShowScrollBar function can also be used to hide or show a scroll bar. It does not affect the scroll bar’s range, page size, or scroll box position.
The EnableScrollBar function can be used to disable one or both arrows of a scroll bar. An application displays disabled arrows in gray and does not respond to user input.
红色标记的地方说道,系统会在两种情况下隐藏并禁用滚动条,1是在SCROLLINFO中的nMin==nMax时,由于最小滚动位置和最大滚动位置相等当然没有必要显示滚动条了,2是在指定的nPage,也就是当前窗口客户区可以显示的单位数据的大小,包含整个滚动范围,这时当前客户区可以显示所有的数据,因此不需要滚动条。MSDN上面是这样描述page size的:
page size代表滚动条的Ower窗口的当前客户区大小可以容纳单位数据的大小,例如如果客户区可以容纳16行文字,那么应该将page size设置为16,系统使用page size,以及滚动范围以及滚动条长度,来设置scroll box的大小,当一个包含滚动条的窗口改变大小时,应该通过SetScrollInfo设置page size。
2**滚动窗口**
进行窗口滚动的操作是在WM_VSCROLL或者WM_HSCROLL中完成的,先根据相应的Request Code对nPos进行调整,然后调用SetScrollInfo对nPos这一项进行调整,之后通过ScrollWindowEx滚动窗口,根据MSDN上面的例子可以看出,先保存先前的nPos,然后用调整后的nPos与之想减,再乘上水平或者垂直方向的单位尺寸,即为滚动的区域。
3**滚动后可见的窗口区域的绘制**
当窗口滚动后,面对的首要问题就是如何显示之前不可见现在可见的区域,对于滚动文字,需要处理当然是需要WM_PAINT了。这里看得不是很懂,但是通过TRACE可以发现的是WM_PAINT确实是在Invalide区域中重绘,当单击垂直滚动条的三角按钮时系统只重绘一行文字,具体方法可以看下面的源码。
下面的源码是MSDN的例子:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
//////////////////////////////////////////////////////////////////////////
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm;
SCROLLINFO si;
// These variables are required to display text.
static int xClient; // width of client area
static int yClient; // height of client area
static int xClientMax; // maximum width of client area
static int xChar; // horizontal scrolling unit
static int yChar; // vertical scrolling unit
static int xUpper; // average width of uppercase letters
static int xPos; // current horizontal scrolling position
static int yPos; // current vertical scrolling position
int i; // loop counter
int x, y; // horizontal and vertical coordinates
int FirstLine; // first line in the invalidated area
int LastLine; // last line in the invalidated area
HRESULT hr;
size_t abcLength=0; // length of an abc[] item
// Create an array of lines to display.
#define LINES 28
static TCHAR *abc[] = {
TEXT(“anteater”), TEXT(“bear”), TEXT(“cougar”),
TEXT(“dingo”), TEXT(“elephant”), TEXT(“falcon”),
TEXT(“gazelle”), TEXT(“hyena”), TEXT(“iguana”),
TEXT(“jackal”), TEXT(“kangaroo”), TEXT(“llama”),
TEXT(“moose”), TEXT(“newt”), TEXT(“octopus”),
TEXT(“penguin”), TEXT(“quail”), TEXT(“rat”),
TEXT(“squid”), TEXT(“tortoise”), TEXT(“urus”),
TEXT(“vole”), TEXT(“walrus”), TEXT(“xylophone”),
TEXT(“yak”), TEXT(“zebra”),
TEXT(“This line contains words, but no character. Go figure.”),
TEXT(“”)
};
//////////////////////////////////////////////////////////////////////////
switch (message)
{
case WM_CREATE:
{
CreateWindowEx(
0L, // no extended styles
_T(“SCROLLBAR”), // scroll bar control class
(LPCWSTR) NULL, // text for window title bar
WS_CHILD | SBS_HORZ, // scroll bar styles
0, // horizontal position
0, // vertical position
200, // width of the scroll bar
50, // default height
hWnd, // handle to main window
(HMENU) NULL, // no menu for a scroll bar
hInst, // instance owning this window
(LPVOID) NULL // pointer not needed
);
//////////////////////////////////////////////////////////////////////////
// Get the handle to the client area’s device context.
hdc = GetDC (hWnd);
// Extract font dimensions from the text metrics.
GetTextMetrics (hdc, &tm);
xChar = tm.tmAveCharWidth;
xUpper = (tm.tmPitchAndFamily & 1 ? 3 : 2) * xChar/2;
yChar = tm.tmHeight + tm.tmExternalLeading;
// Free the device context.
ReleaseDC (hWnd, hdc);
// Set an arbitrary maximum width for client area.
// (xClientMax is the sum of the widths of 48 average
// lowercase letters and 12 uppercase letters.)
xClientMax = 48 xChar + 12 xUpper;
return 0;
//////////////////////////////////////////////////////////////////////////
}
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_SIZE:
{
// Retrieve the dimensions of the client area.
yClient = HIWORD (lParam);
xClient = LOWORD (lParam);
// Set the vertical scrolling range and page size
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = LINES - 1;
si.nPage = yClient / yChar;
SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
// Set the horizontal scrolling range and page size.
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_PAGE;
si.nMin = 0;
si.nMax = 2 + xClientMax / xChar;
si.nPage = xClient / xChar;
SetScrollInfo(hWnd, SB_HORZ, &si, TRUE);
return 0;
}
case WM_HSCROLL:
{
// Get all the vertial scroll bar information
si.cbSize = sizeof (si);
si.fMask = SIF_ALL;
// Save the position for comparison later on
GetScrollInfo (hWnd, SB_HORZ, &si);
xPos = si.nPos;
switch (LOWORD (wParam))
{
// user clicked left arrow
case SB_LINELEFT:
si.nPos -= 1;
break;
// user clicked right arrow
case SB_LINERIGHT:
si.nPos += 1;
break;
// user clicked the scroll bar shaft left of the scroll box
case SB_PAGELEFT:
si.nPos -= si.nPage;
break;
// user clicked the scroll bar shaft right of the scroll box
case SB_PAGERIGHT:
si.nPos += si.nPage;
break;
// user dragged the scroll box
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_HORZ, &si, TRUE);
GetScrollInfo (hWnd, SB_HORZ, &si);
// If the position has changed, scroll the window
if (si.nPos != xPos)
{
ScrollWindowEx(hWnd, xChar * (xPos - si.nPos), 0, NULL, NULL,
NULL, NULL,SW_INVALIDATE|SW_ERASE);
//ScrollWindow(hWnd, xChar * (xPos - si.nPos), 0, NULL, NULL);
}
return 0;
}
case WM_VSCROLL:
{
// Get all the vertial scroll bar information
si.cbSize = sizeof (si);
si.fMask = SIF_ALL;
GetScrollInfo (hWnd, SB_VERT, &si);
// Save the position for comparison later on
yPos = si.nPos;
switch (LOWORD (wParam))
{
// user clicked the HOME keyboard key
case SB_TOP:
si.nPos = si.nMin;
break;
// user clicked the END keyboard key
case SB_BOTTOM:
si.nPos = si.nMax;
break;
// user clicked the top arrow
case SB_LINEUP:
si.nPos -= 1;
break;
// user clicked the bottom arrow
case SB_LINEDOWN:
si.nPos += 1;
break;
// user clicked the scroll bar shaft above the scroll box
case SB_PAGEUP:
si.nPos -= si.nPage;
break;
// user clicked the scroll bar shaft below the scroll box
case SB_PAGEDOWN:
si.nPos += si.nPage;
break;
// user dragged the scroll box
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 window and update it
if (si.nPos != yPos)
{
ScrollWindow(hWnd, 0, yChar * (yPos - si.nPos), NULL, NULL);
UpdateWindow (hWnd);
}
return 0;
}
case WM_PAINT:
{
// Prepare the window for painting
hdc = BeginPaint (hWnd, &ps);
// Get vertical scroll bar position
si.cbSize = sizeof (si);
si.fMask = SIF_POS;
GetScrollInfo (hWnd, SB_VERT, &si);
yPos = si.nPos;
// Get horizontal scroll bar position
GetScrollInfo (hWnd, SB_HORZ, &si);
xPos = si.nPos;
// Find painting limits
FirstLine = max (0, yPos + ps.rcPaint.top / yChar);
LastLine = min (LINES - 1, yPos + ps.rcPaint.bottom / yChar);
sprintf(szDebug,(“%d,%d,%d,%d/r/n”),ps.rcPaint.left,ps.rcPaint.top,ps.rcPaint.right,ps.rcPaint.bottom);
OutputDebugStringA(szDebug);
ZeroMemory(szDebug,0);
sprintf(szDebug,“FirstLine=%d,LastLine=%d/r/n”,FirstLine,LastLine);
OutputDebugStringA(szDebug);
for (i = FirstLine; i <= LastLine; i++)
{
x = xChar * (1 - xPos);
y = yChar * (i - yPos);
// Note that “55” in the following depends on the
// maximum size of an abc[] item.
//
hr = StringCchLength(abc[i], 100, &abcLength);
if ((FAILED(hr)) | (&abcLength == NULL))
{
/MessageBox(hWnd,_T(“字符串长度为!”),_T(“错误“),MB_ICONERROR);/
}
else
{
TextOut(hdc,x,y,abc[i],abcLength);
}
}
// Indicate that painting is finished
EndPaint (hWnd, &ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}