用以下一个结构和三个函数就能设置滚动条了
1.滚动条信息结构
//SetScrollInfo函数设置这个结构的信息
//GetScrollInfo函数返回这个结构的信息
typedef struct tagSCROLLINFO {
UINT cbSize; //存储这个结构的大小,以字节为单位
UINT fMask; //标志这个结构的哪些值被设置或被获取
int nMin; //滚动条的最小位置
int nMax; //滚动条的最大位置//一般这两个参数一起构成滚动条范围
UINT nPage; //页面大小
int nPos; //滚动条位置
int nTrackPos; //可以随着用户的拖动而显示当前滚动条位置
} SCROLLINFO;
typedef SCROLLINFO FAR* LPSCROLLINFO;
/* fMask: SIF_ALL:结构中参数全部有效(都可以被用) SIF_DISABLENOSCROLL:这个值是只有当设置滚动条的参数使用。如果滚动条的新参数使滚动条不必要的,禁用滚动条,而不是删除它。 SIF_PAGE:nPage有效 SIF_POS:nPos有效 SIF_RANGE:nMin,nMax有效 SIF_TRACKPOS:nTrackPos有效 */
2.获取滚动条信息
BOOL GetScrollInfo(
HWND hwnd, //窗口句柄
int fnBar, //指定返回哪个滚动条的参数,SB_CTL(控件),SB_HORZ(水平滚动条),SB_VERT(垂直滚动条)
LPSCROLLINFO lpsi //滚动条信息结构
);
3.设置滚动条信息
int SetScrollInfo(
HWND hwnd, //窗口句柄
int fnBar, //指定哪个滚动条的参数
LPSCROLLINFO lpsi, //滚动条信息结构
BOOL fRedraw //是否重画滚动条,也就是即时显示滚动条
//TRUE表示重画,FALSE表示不重画
);
4.滚动窗口
BOOL ScrollWindow(
HWND hWnd, //窗口句柄
int XAmount, //指定水平滚动以设备为单位的数量。如果窗体被滚动模式为
//CS_OWNDC或CS_CLASSDC,此参数则使用逻辑单位而不使用设备单位。
//当向左滚动窗体内容时,参数值必须为负。
int YAmount, //指定垂直滚动以设备为单位的数量。
const RECT *lpRect,//指向所指定将被滚动的客户区域部分的RECT结构。若此参数为NULL,则整个客户区域均被滚动。
const RECT *lpClipRect//指向包含类似于剪辑滚动条RECT结构。只有剪辑矩形条内部的位受影响。
//由外向内的滚动矩形内部被着色,而由矩形内向外的滚动将不被着色。
);
主要步骤:
1.向窗口添加滚动条风格
2.WM_CREATE消息中设置字体信息.
3.在WM_SZIE消息中获取客户区宽度和高度
4.在WM_SIZE消息中设置滚动条范围和位置
5.在WM_VSCROLL和WM_HSCROLL消息中滚动滑块的位置
6.在WM_PAINT消息中显示滚动滑块后的内容
详细代码如下:
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//全局变量存储错误信息
DWORD dwError = 0;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("Scroll");
HWND hwnd=nullptr;
MSG msg = {0};
WNDCLASS wndclass = {0};
//创建窗口类
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = nullptr;
wndclass.lpszClassName = szAppName;
//注册窗口类
if (!RegisterClass(&wndclass))
{
MessageBox(nullptr, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
//创建窗口
hwnd = CreateWindow(szAppName, TEXT("ScrollDemo"),
WS_OVERLAPPEDWINDOW|WS_HSCROLL|WS_VSCROLL,//添加滚动条风格
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
nullptr, nullptr, hInstance, nullptr);
if (!hwnd)
{
dwError = GetLastError();
return 0;
}
//显示窗口
ShowWindow(hwnd, iCmdShow);
//更新窗口
UpdateWindow(hwnd);
//获取队列中的消息
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//字体的宽度,高度,cxCaps是宽字符的宽度
static int cxChar = 0, cxCaps = 0, cyChar = 0;
//滚动条的垂直位置和水平位置
static int iVertPos = 0, iHorzPos = 0;
//获取当前客户区可容纳的行数和列数
static int cxColumn = 0, cyLine = 0;
//客户区宽度和高度
static int cxClient = 0, cyClient = 0;
//鼠标滑动信息
static int iDeltaPerLine = 0, iAccumDelta = 0;
ULONG ulScrollLines = 0;
int i = 0, temp = 0, ix = 0, iy = 0;
//设备环境句柄
HDC hdc = nullptr;
//绘图信息结构
PAINTSTRUCT ps = { 0 };
//字体信息结构
TEXTMETRIC tm = { 0 };
//滚动条信息结构
SCROLLINFO si = {0};
switch (message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
//获取字体信息,一般系统默认
GetTextMetrics(hdc, &tm);
//字符宽度
cxChar = tm.tmAveCharWidth;
//判断是否是宽字符,宽字符就是1.5cxChar
cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
//字符高度
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hwnd, hdc);
case WM_SETTINGCHANGE:
//SPI_GETWHEELSCROLLLINES:用于Windows NT 4.0及以后版本、
//Windows 98。当前轨迹球转动时,获取滚动的行数。参数pvParam
//必须指向UINT类型变量以接收行数。缺省值是3。
if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &ulScrollLines, 0))
{
dwError = GetLastError();
break;
}
// 增量数值用WHEEL_DELTA来标识,它等于120
if (ulScrollLines)//每滚动一行的增量是40
iDeltaPerLine = WHEEL_DELTA / ulScrollLines;
else
iDeltaPerLine = 0;
return 0;
case WM_SIZE:
//获取客户区宽度和高度,每当你改变窗口大小的时候它
//都会发生改变。
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
//获取当前客户区可容纳的列数
cxColumn = cxClient / cxChar;
//获取当前客户区可容纳的行数
cyLine = cyClient / cyChar;
//设置垂直滚动条范围和初始位置
si.cbSize = sizeof(si);//要使用这个结构,必须要把它的大小传入进去
si.fMask = SIF_RANGE | SIF_PAGE|SIF_POS;//范围、页面大小和位置三个参数有效
si.nMin = 0;//设置范围的最小值
si.nMax = cyClient/cyChar+200;//设置范围的最大值后面的根据实际值来加
si.nPos = 0;//设置滚动条初始位置
si.nPage= cyClient / cyChar;//设置页面大小
//用SetScrollInfo函数设置垂直滚动条信息(结构中的值传入函数)
SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
//设置水平滚动条范围和初始位置
si.cbSize = sizeof(si);//要使用这个结构,必须要把它的大小传入进去
si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;//范围、页面大小和位置三个参数有效
si.nMin = 0;//设置范围的最小值
si.nMax = cxClient / cxChar+20;//设置范围的最大值
si.nPos = 0;//设置滚动条初始位置
si.nPage = cxClient / cxChar;//设置页面大小
//用SetScrollInfo函数设置水平滚动条信息(结构中的值传入函数)
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_TOP:
si.nPos = si.nMin;
break;
case SB_BOTTOM:
si.nPos = si.nMax;
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);
}
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;
default:
break;
}
//把新的滑块位置保存到结构中
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, nullptr, nullptr);
UpdateWindow(hwnd);
}
return 0;
//键盘消息
case WM_KEYDOWN:
//如果有按下方向键
switch (LOWORD(wParam))
{
//如果按下方向键上
case VK_UP:
//想窗口过程发送消息
SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
break;
case VK_DOWN:
SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
break;
case VK_LEFT:
SendMessage(hwnd, WM_HSCROLL, SB_LINELEFT, 0);
break;
case VK_RIGHT:
SendMessage(hwnd, WM_HSCROLL, SB_LINERIGHT, 0);
break;
//PageUp
case VK_PRIOR:
SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
break;
//PageDown
case VK_NEXT:
SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
//Bottom
case VK_END:
SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0);
break;
case VK_HOME:
SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);
break;
default:
break;
}
return 0;
//鼠标滚轮消息
case WM_MOUSEWHEEL:
//如果增量等于0就是没有滚动就退出
if (iDeltaPerLine == 0)
break;
iAccumDelta += (short)HIWORD(wParam); // 120 or -120
while (iAccumDelta >= iDeltaPerLine)
{
SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
iAccumDelta -= iDeltaPerLine;
}
while (iAccumDelta <= -iDeltaPerLine)
{
SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
iAccumDelta += iDeltaPerLine;
}
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
//获取垂直滚动条位置
si.cbSize = sizeof(si);
si.fMask = SIF_POS;
GetScrollInfo(hwnd, SB_VERT, &si);
iVertPos = si.nPos;
// 获取水平滚动条位置
GetScrollInfo(hwnd, SB_HORZ, &si);
iHorzPos = si.nPos;
for (int i = 0;i < 70;i++)
{
ix = cxChar*(1 - iHorzPos);
iy = cyChar*(i - iVertPos);
TextOut(hdc,ix,iy,L"1234567845623132",wcslen(L"1234567845623132") );
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
要注意几点:
1.设置范围si.nMax的时候,它决定了你能滑动多少行。比如你要显示100行,但是客户区只能显示50行,那么你的si.nMax就要设置为cyClient/cyChar(客户区大小)+50(或大于50)才能显示完.
2.初始位置可以自己设置,一般系统默认初始化为0了。
3.si.cbSize一点要设置,不管是在设置或获取结构信息的时候,都要把结构体大小传入。
4.在WM_VSCROLL和WM_HSCROLL中,处理滑块移动前要把当前位置保存下来,处理完后把现在的位置保存起来,比较是否一样,不一样代表滑块移动了,就滚动窗口。
这些都是手动设置滚动条的范围,而不能根据输入多少文本,滚动条范围随之改变。