问题描述
我的英语并不完美。我正在使用 Visual C++ 2019 和 MFC。示例程序:一个SDI程序,视图的基数为CScrollView
,在一个矩阵中绘制128*128个0。但是 MFC 在使用滚动条滚动时不会清除背景。你有什么想法吗?谢谢。
在 Windows 设置中,我使用的是 96 dpi * 3 = 288 dpi。 我试过:96 dpi 模式受影响。
如何将示例程序上传到此?
void CsdView::OnDraw(CDC *pDC) {
CsdDoc *pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CRect rect;
GetClientRect(&rect);
pDC->FillSolidRect(rect,0xFFFFFF);
CPoint pos = GetDeviceScrollPosition();
TRACE(L"OnDraw: %4u.%4u - %4u.%4u,%4u.%4u\n",rect.right,rect.bottom,pos.x,pos.y,rect.right + pos.x,rect.bottom+pos.y);
for (int i = 0; i < 128; ++ i)
for (int j = 0; j < 128; ++ j)
pDC->textoutW(j*20 - pos.x,i*54 - pos.y,L"0",1);
}
void CsdView::OnInitialUpdate() {
CScrollView::OnInitialUpdate();
CSize sizetotal;
sizetotal.cx = 20*128;
sizetotal.cy = 54*128;
SetScrollSizes(MM_TEXT,sizetotal);
}
BOOL CsdView::OnEraseBkgnd(CDC *pDC) {
CBrush brush(0xFFFFFF);
FillOutsideRect(pDC,&brush);
return TRUE;
// return CScrollView::OnEraseBkgnd(pDC);
}
void CIDEView::OnDraw(CDC *pDC) {
CIDEDoc *const d = GetDocument();
ASSERT_VALID(d);
if (! d)
return;
CPoint const pos = GetDeviceScrollPosition();
CRect rect;
GetClientRect(&rect);
OffsetRect(&rect,pos.y);
pDC->FillSolidRect(rect,bkcolor);
auto oldfont = pDC->SelectObject(&font);
pDC->SetBkColor(bkcolor);
pDC->SetTextColor(textcolor);
pDC->SetBkMode(TRANSPARENT);
const int cxs = pos.x / mincw,cys = pos.y / lineheight;
const int cxe = (rect.right + mincw-1) / mincw,cye = (rect.bottom + 41) / lineheight;
for (int i = cys; i <= cye; ++ i)
for (int j = cxs; j <= cxe; ++ j)
pDC->textoutW(textmargin+j*mincw,i*lineheight,1);
pDC->SelectObject(oldfont);
}
void CIDEView::OnInitialUpdate() {
CScrollView::OnInitialUpdate();
SetScrollSizes(MM_TEXT,{linewidth,128*lineheight});
}
BOOL CIDEView::OnEraseBkgnd(CDC *pDC) {
return TRUE;
}
解决方法
CScrollView
类是具有滚动功能的视图。您几乎可以像使用 CView
一样使用它(即在 OnDraw()
成员中绘图),只是您必须考虑可能的滚动。
GetClientRect()
函数返回可见客户区,返回的坐标不是相对于视图原点,而是相对于窗口原点,即 left
和 top
成员总是0. CDC
函数中的 OnDraw()
参数虽然是相对于逻辑视图原点,所以需要调整。
至于您的代码,您不需要使用 OnEraseBkgnd()
函数,因为您是在 OnDraw()
中这样做的。您只填充窗口的可见部分,但这非常好。所以最好把它去掉。此外,传递给 TextOutW()
函数的坐标必须相对于视图原点,因此 -pos.x
和 -pos.y
调整是错误的。相反,需要调整的是传递给 FillSolidRect()
函数的矩形。所以,你的代码会变成:
void CsdView::OnDraw(CDC *pDC)
{
CsdDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CPoint pos = GetScrollPosition();
CRect rect;
GetClientRect(&rect);
// Adjust client rect to device coordinates
OffsetRect(&rect,pos.x,pos.y);
pDC->FillSolidRect(rect,0xFFFFFF);
for (int i = 0; i < 128; ++i)
for (int j = 0; j < 128; ++j)
pDC->TextOutW(j * 20,i * 54,L"0",1);
}
然而,这段代码是“浪费的”,因为它绘制了整个视图。它可以优化为仅绘制可见部分。它只会绘制 0,即使是一个像素位于可见部分(没有 #define
任何东西,只是使用了您的硬编码 20 和 54 值)。也把颜色改成黄色,这样可以更好的测试一下(白色是默认背景色)。
void CsdView::OnDraw(CDC *pDC)
{
CsdDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CPoint pos = GetScrollPosition();
CRect rect;
GetClientRect(&rect);
// Adjust client rect to device coordinates
OffsetRect(&rect,0x00FFFF);
// Paint only the items in the visible part
int xL = rect.left / 20,xR = (rect.right + 19) / 20,yT = rect.top / 54,yB = (rect.bottom + 53) / 54;
for (int i = yT; i < yB; ++i)
for (int j = xL; j < xR; ++j)
pDC->TextOutW(j * 20,1);
}
编辑:
修改后的代码中,为什么doc、pos、cxs等变量是const
?你的逻辑也有不少错误:
- 您将视图大小设置为
OnInitialUpdate()
,而不是假设linewidth
等于textmargin + 128*mincw
。如果没有,请再次修改您的代码。 -
rect
已调整(使用OffsetRect()
),因此再次添加pos.x
是错误的。 - 因为您在变量中有单元格大小,所以不要使用硬编码数字。例如,
cxe
的代码应该变成cxe = (rect.right + mincw - 1) / mincw
;类似地更新cye
代码。 - 您还以
textmargin
的偏移量进行绘制。然后代码应该变成cxe = (rect.right - textmargin + mincw - 1) / mincw
; - 我发布的代码适用于循环中的
<
条件,您不需要<=
。算一算,你会发现这是正确的。