如何计算CListCtrl内适配显示的缩略图正确尺寸避免水平滚动条
修复CListCtrl缩略图水平滚动条问题
问题根因
直接调用GetClientRect取控件宽度计算缩略图尺寸仍出现滚动条,是漏算了三类固定占用:
CListCtrl内置的项边距、边框留白:大图标模式下控件默认给每个项预留了图标周围的内边距,直接用全客户区宽度当缩略图宽度,项实际渲染宽度会超出可视范围- 垂直滚动条占用宽度:当列表项总高度超过控件可视高度时,系统会自动预留垂直滚动条的显示位置,这部分宽度不会算在初始客户区的可用内容宽度里
- 硬编码值适配性差:之前写死缩进25像素,只是刚好凑对了100%缩放下默认主题的边距+滚动条宽度,在高DPI、不同系统主题下数值完全不匹配
分步修复
1. 正确计算可用内容宽度
不要直接用GetClientRect返回的全宽度,先扣除不可用区域,所有尺寸用系统API获取,不要写死像素值:
CRect rcList; m_ListThumbnail.GetClientRect(&rcList); // 扣除垂直滚动条占用宽度 if (GetWindowLong(m_ListThumbnail.GetSafeHwnd(), GWL_STYLE) & WS_VSCROLL) { rcList.right -= GetSystemMetrics(SM_CXVSCROLL); } // 扣除控件边框的留白 rcList.DeflateRect(GetSystemMetrics(SM_CXEDGE) * 2, GetSystemMetrics(SM_CYEDGE) * 2); int iAvailWidth = rcList.Width();
如果场景是每行显示一个显示器缩略图,更简单的方案是把CListCtrl切到单列表报表模式,初始化时把列宽设为LVSCW_AUTOSIZE_USEHEADER,控件会自动适配可用宽度,从根源避免宽度计算偏差:
// 初始化控件时设置风格 m_ListThumbnail.ModifyStyle(LVS_TYPEMASK, LVS_REPORT | LVS_NOCOLUMNHEADER); m_ListThumbnail.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER); // 插入占满整行的列 m_ListThumbnail.InsertColumn(0, L"", 0, LVSCW_AUTOSIZE_USEHEADER); // 主动隐藏水平滚动条 m_ListThumbnail.ShowScrollBar(SB_HORZ, FALSE);
2. 按源图比例动态计算缩略图尺寸
不要固定用主显示器的宽高比算全局缩略图高度,否则遇到竖屏、超宽屏显示器的截图时,要么高度溢出要么出现多余留白:
// 先按常用16:9比例算基准行高,上下留够边距 int iBaseRowHeight = (int)(iAvailWidth * 9.0 / 16.0) + GetSystemMetrics(SM_CYEDGE) * 4; // 遍历每个显示器的捕获区域单独计算尺寸 for (auto& rcCapture : m_allMonitorRc) { double dAspect = (double)rcCapture.Width() / rcCapture.Height(); int nThumbW, nThumbH; // 优先按可用宽度适配 nThumbW = iAvailWidth; nThumbH = (int)(nThumbW / dAspect); // 如果高度超出基准行高,切换为按高度适配,保证不超出行范围 if (nThumbH > iBaseRowHeight) { nThumbH = iBaseRowHeight; nThumbW = (int)(nThumbH * dAspect); } // 后续渲染沿用原有的居中逻辑即可 }
3. 渲染逻辑适配
原有StretchBlt渲染逻辑不需要大改,注意SetStretchBltMode用HALFTONE比COLORONCOLOR缩放画质更好,缩小截图时不会出现明显锯齿:
SetStretchBltMode(dcDraw, HALFTONE); SetBrushOrgEx(dcDraw, 0, 0, nullptr); int iLeft = (iAvailWidth - nThumbW) / 2; int iTop = (iBaseRowHeight - nThumbH) / 2; ::StretchBlt(dcDraw, iLeft, iTop, nThumbW, nThumbH, dcScreen.m_hDC, rcCapture.left, rcCapture.top, rcCapture.Width(), rcCapture.Height(), SRCCOPY | CAPTUREBLT);
内容的提问来源于stack exchange,提问作者Andrew Truckle
相关产品推荐
相关产品推荐

