基于CScrollView的IDE视图用CreateCompatibleDC双缓冲不显示字符问题咨询
双缓冲显示异常的核心原因与修复
存在两个关键逻辑错误,直接导致绘制内容不可见:
- 内存DC未关联有效绘制位图
调用CreateCompatibleDC仅会创建内存绘制上下文,默认选入的是1x1的单色占位位图,没有匹配你需要绘制的客户区大小。后续FillSolidRect、TextOut等操作都会因为超出占位位图的存储范围被直接丢弃,无实际绘制内容留存。
修复方式:创建内存DC后,生成与显示DC兼容、和当前客户区大小完全匹配的位图,再选入内存DC作为绘制目标:// 获取客户区真实设备坐标,无需做偏移 CRect clientRect; GetClientRect(&clientRect); CDC memDc; if (!memDc.CreateCompatibleDC(pDC)) { MessageBox(L"CreateCompatibleDC is failed"); return; } // 创建与客户区等大的兼容位图 CBitmap memBmp; memBmp.CreateCompatibleBitmap(pDC, clientRect.Width(), clientRect.Height()); // 选入内存DC,保存旧GDI对象避免资源泄漏 CBitmap* pOldBmp = memDc.SelectObject(&memBmp); CFont* pOldFont = memDc.SelectObject(&font); - BitBlt目标坐标与源坐标逻辑错误
BitBlt的目标坐标是显示DC的客户区设备坐标,原点固定为客户区左上角(0,0),而非文档滚动后的偏移位置。你原代码中给BitBlt传了pos.x, pos.y作为目标坐标,所有绘制内容都会被放到客户区可视范围之外,自然无法看到。
同时,绘制文本的坐标逻辑也需要修正:需要将文档坐标减去滚动偏移量,转换为当前可见区域内的相对坐标后,再在内存DC上绘制。
修正后的完整OnDraw实现示例:
优化建议:可将内存DC、内存位图设为视图类的成员变量,仅在客户区大小变更时重新创建,避免每次重绘都反复创建销毁GDI对象带来的开销。void CIDEView::OnDraw(CDC *pDC) { CPoint const scrollPos = GetDeviceScrollPosition(); CRect clientRect; GetClientRect(&clientRect); CDC memDc; if (!memDc.CreateCompatibleDC(pDC)) { MessageBox(L"CreateCompatibleDC is failed"); return; } CBitmap memBmp; memBmp.CreateCompatibleBitmap(pDC, clientRect.Width(), clientRect.Height()); CBitmap* pOldBmp = memDc.SelectObject(&memBmp); CFont* pOldFont = memDc.SelectObject(&font); // 填充编辑器背景 memDc.FillSolidRect(&clientRect, RGB(255, 255, 255)); // 示例文本绘制:文档坐标转换为当前可视区域的相对坐标 memDc.TextOutW(0, 0, L"¶", 1); // 恢复GDI对象 memDc.SelectObject(pOldFont); memDc.SelectObject(pOldBmp); // 一次性将内存绘制内容拷贝到显示DC的客户区原点 pDC->BitBlt(0, 0, clientRect.Width(), clientRect.Height(), &memDc, 0, 0, SRCCOPY); }
内存占用疑问解答
你担心的20倍桌面分辨率内存占用的情况不会发生:
- 常规双缓冲的内存位图,仅需要和当前可见的客户区大小匹配,不需要和整个文档的总大小匹配。不管文档有多少页,每次重绘时仅需绘制当前屏幕能显示的部分内容,分配的内存位图大小最多等于当前客户区的尺寸。
- 按3840*2160分辨率、32位色深计算,单帧内存占用为
3840 * 2160 * 4 ≈ 32MB,与文档总页数无关联。 - 若需进一步降低内存占用,可仅为本次重绘的无效区域(而非整个客户区)分配对应大小的内存位图,进一步减少临时内存开销。
内容的提问来源于stack exchange,提问作者Zoltán Hegedüs
相关产品推荐
相关产品推荐

