假设对话框类名 CMyDialog,继承自 CDialog 或 CDialogEx。
CMyDialog 添加WM_MOUSEWHEEL、WM_VSCROLL、WM_HSCROLL 三个消息映射函数。并添加如下一个自定义成员函数。
void UpdateScrollInfo(int nWidthNeed, int nLengthNeed);
之后添加或修改滚动条时直接调用该函数即可。
四个函数的实现代码如下:
BOOL CMyDialog::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
if ((GetStyle() & WS_VSCROLL) == WS_VSCROLL)
{
SCROLLINFO scrinf;
GetScrollInfo(SB_VERT, &scrinf, SIF_ALL);
// 旧窗体位置(窗体尺寸及滚动位置根据 SCROLLINFO::nMax、nPage、nPos 反算,省去增加并维护相关成员变量的代码)
int nWndPosOld = scrinf.nPos * scrinf.nMax / (scrinf.nMax - (int)scrinf.nPage);
nWndPosOld = min(max(nWndPosOld, 0), scrinf.nMax);
// 移动滚动条
scrinf.nPos -= zDelta;
SetScrollInfo(SB_VERT, &scrinf);
// 新窗体位置(窗体尺寸及滚动位置根据 SCROLLINFO::nMax、nPage、nPos 反算,省去增加并维护相关成员变量的代码)
int nWndPosNew = scrinf.nPos * scrinf.nMax / (scrinf.nMax - (int)scrinf.nPage);
nWndPosNew = min(max(nWndPosNew, 0), scrinf.nMax);
// 滚动窗体
ScrollWindow(0, nWndPosOld - nWndPosNew);
return TRUE;
}
else if ((GetStyle() & WS_HSCROLL) == WS_HSCROLL)
{
SCROLLINFO scrinf;
GetScrollInfo(SB_HORZ, &scrinf, SIF_ALL);
// 旧窗体位置(窗体尺寸及滚动位置根据 SCROLLINFO::nMax、nPage、nPos 反算,省去增加并维护相关成员变量的代码)
int nWndPosOld = scrinf.nPos * scrinf.nMax / (scrinf.nMax - (int)scrinf.nPage);
nWndPosOld = min(max(nWndPosOld, 0), scrinf.nMax);
// 移动滚动条
scrinf.nPos -= zDelta;
SetScrollInfo(SB_HORZ, &scrinf);
// 新窗体位置(窗体尺寸及滚动位置根据 SCROLLINFO::nMax、nPage、nPos 反算,省去增加并维护相关成员变量的代码)
int nWndPosNew = scrinf.nPos * scrinf.nMax / (scrinf.nMax - (int)scrinf.nPage);
nWndPosNew = min(max(nWndPosNew, 0), scrinf.nMax);
// 滚动窗体
ScrollWindow(nWndPosOld - nWndPosNew, 0);
return TRUE;
}
return CDialog::OnMouseWheel(nFlags, zDelta, pt);
}
void CMyDialog::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if ((GetStyle() & WS_VSCROLL) == WS_VSCROLL)
{
SCROLLINFO scrinf = { 0 };
GetScrollInfo(SB_VERT, &scrinf, SIF_ALL);
// 旧窗体位置(窗体尺寸及滚动位置根据 SCROLLINFO::nMax、nPage、nPos 反算,省去增加并维护相关成员变量的代码)
int nWndPosOld = scrinf.nPos * scrinf.nMax / (scrinf.nMax - (int)scrinf.nPage);
nWndPosOld = min(max(nWndPosOld, 0), scrinf.nMax);
// 滚动条最大滚动位置
int nPosMax = scrinf.nMax - (int)scrinf.nPage;
switch (nSBCode)
{
case SB_TOP: scrinf.nPos = 0; break; // 滑块滚动到最顶部
case SB_BOTTOM: scrinf.nPos = nPosMax; break; // 滑块滚动到最底部
case SB_LINEUP: scrinf.nPos = scrinf.nPos - nPosMax; break; // 单击上箭头
case SB_LINEDOWN: scrinf.nPos = scrinf.nPos + nPosMax; break; // 单击下箭头
case SB_PAGEUP: scrinf.nPos -= nPosMax / 2; break; // 单击滑块上方空白区域
case SB_PAGEDOWN: scrinf.nPos += nPosMax / 2; break; // 单击滑块下方空白区域
case SB_THUMBTRACK: scrinf.nPos = nPos; break; // 拖动滑块
}
// 移动滚动条
scrinf.fMask = SIF_POS;
scrinf.nPos = min(max(scrinf.nPos, 0), nPosMax);
SetScrollInfo(SB_VERT, &scrinf);
// 新窗体位置(窗体尺寸及滚动位置根据 SCROLLINFO::nMax、nPage、nPos 反算,省去增加并维护相关成员变量的代码)
int nWndPosNew = scrinf.nPos * scrinf.nMax / (scrinf.nMax - (int)scrinf.nPage);
nWndPosNew = min(max(nWndPosNew, 0), scrinf.nMax);
// 滚动窗体
ScrollWindow(0, nWndPosOld - nWndPosNew);
}
CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
}
void CMyDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if ((GetStyle() & WS_HSCROLL) == WS_HSCROLL)
{
SCROLLINFO scrinf;
GetScrollInfo(SB_HORZ, &scrinf, SIF_ALL);
// 旧窗体位置(窗体尺寸及滚动位置根据 SCROLLINFO::nMax、nPage、nPos 反算,省去增加并维护相关成员变量的代码)
int nWndPosOld = scrinf.nPos * scrinf.nMax / (scrinf.nMax - (int)scrinf.nPage);
nWndPosOld = min(max(nWndPosOld, 0), scrinf.nMax);
// 滚动条最大滚动位置
int nPosMax = scrinf.nMax - (int)scrinf.nPage;
switch (nSBCode)
{
case SB_LEFT: scrinf.nPos = 0; break; // 滑块滚动到最左部
case SB_RIGHT: scrinf.nPos = nPosMax; break; // 滑块滚动到最右部
case SB_LINELEFT: scrinf.nPos = scrinf.nPos - nPosMax; break; // 单击左箭头
case SB_LINERIGHT: scrinf.nPos = scrinf.nPos + nPosMax; break; // 单击右箭头
case SB_PAGELEFT: scrinf.nPos -= nPosMax / 2; break; // 单击滑块左空白区域
case SB_PAGERIGHT: scrinf.nPos += nPosMax / 2; break; // 单击滑块右空白区域
case SB_THUMBTRACK: scrinf.nPos = nPos; break; // 拖动滑块
}
// 移动滚动条
scrinf.fMask = SIF_POS;
scrinf.nPos = min(max(scrinf.nPos, 0), nPosMax);
SetScrollInfo(SB_HORZ, &scrinf);
// 新窗体位置(窗体尺寸及滚动位置根据 SCROLLINFO::nMax、nPage、nPos 反算,省去增加并维护相关成员变量的代码)
int nWndPosNew = scrinf.nPos * scrinf.nMax / (scrinf.nMax - (int)scrinf.nPage);
nWndPosNew = min(max(nWndPosNew, 0), scrinf.nMax);
// 滚动窗体
ScrollWindow(nWndPosOld - nWndPosNew, 0);
}
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
// 更新滚动条(不改变原滚动位置)
void CMyDialog::UpdateScrollInfo(int nWidthNeed, int nLengthNeed)
{
ASSERT(nWidthNeed > 0);
ASSERT(nLengthNeed > 0);
int nCxVsCroll = ::GetSystemMetrics(SM_CXVSCROLL); // 系统垂直滚动条宽度
int nCyHsCroll = ::GetSystemMetrics(SM_CYHSCROLL); // 系统水平滚动条高度
// 可用客户区是去掉滚动条的客户区,计算滚动条参数时需要知道更新前和后滚动条是否存在,以在计算时减去或加上滚动条的尺寸
// 当前客户区,这是含滚动条的尺寸(如果有)
CRect rcClient;
GetClientRect(rcClient);
// 如果当前有垂直滚动条,则可用客户区宽度加上垂直滚动条宽度,如果当前有水平滚动条,则可用客户区高度加上水平滚动条高度
if ((GetStyle() & WS_VSCROLL) == WS_VSCROLL) rcClient.right += nCxVsCroll;
if ((GetStyle() & WS_HSCROLL) == WS_HSCROLL) rcClient.bottom += nCyHsCroll;
// 1、计算是否需要添加垂直滚动条
BOOL bVsCrollAdd = nLengthNeed > rcClient.Height();
// 如果需要垂直滚动条,客户区宽度减去垂直滚动条宽度
if (bVsCrollAdd)
rcClient.right -= nCxVsCroll;
// 2、计算是否需要添加水平滚动条
BOOL bHsCrollAdd = nWidthNeed > rcClient.Width();
// 如果需要水平滚动条,客户区高度减去水平滚动条宽度
if (bHsCrollAdd)
rcClient.bottom -= nCyHsCroll;
// 3、如果需要水平滚动条并且前面计算不需要垂直滚动条,则由于可用客户区高度已变化,需要重新计算是否需要垂直滚动条
if (bHsCrollAdd && !bVsCrollAdd)
{
bVsCrollAdd = nLengthNeed > rcClient.Height();
// 如果需要垂直滚动条,客户区宽度减去垂直滚动条宽度
if (bVsCrollAdd)
rcClient.right -= nCxVsCroll;
}
SCROLLINFO scinfo = { 0 };
scinfo.cbSize = sizeof(SCROLLINFO);
scinfo.fMask = SIF_RANGE | SIF_PAGE;
// 更新垂直滚动条(不改变原滚动位置)
scinfo.nMax = max(0, nLengthNeed - rcClient.Height()); // 垂直滚动最大值 = 需求高度 - 客户区高度
scinfo.nPage = scinfo.nMax * rcClient.Height() / nLengthNeed; // 垂直滚动条内的滚动块高度
SetScrollInfo(SB_VERT, &scinfo);
// 更新水平滚动条(不改变原滚动位置)
scinfo.nMax = max(0, nWidthNeed - rcClient.Width()); // 水平滚动最大值 = 需求宽度 - 客户区宽度
scinfo.nPage = scinfo.nMax * rcClient.Width() / nWidthNeed; // 水平滚动条内的滚动块长度
SetScrollInfo(SB_HORZ, &scinfo);
}
最后需注意在计算可用客户区时要注意加减滚动条所占用的尺寸,如:
CRect rcClient;
GetClientRect(rcClient);
// 如果当前有垂直滚动条,则可用客户区减去垂直滚动条宽度
if ((GetStyle() & WS_VSCROLL) == WS_VSCROLL)
rcClient.right -= ::GetSystemMetrics(SM_CXVSCROLL);
// 如果当前有水平滚动条,则可用客户区减去水平滚动条高度
if ((GetStyle() & WS_HSCROLL) == WS_HSCROLL)
rcClient.bottom -= ::GetSystemMetrics(SM_CYHSCROLL);
来源:CSDN
作者:bigwasp
链接:https://blog.csdn.net/bigwasp/article/details/104750044