MFC CScrollView 不清除背景
MFC CScrollView does not clear background
我的英语并不完美。我正在使用 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);
}
我无法上传图片和代码作为评论,所以我必须编辑原问题。
还有一个小bug。原代码(MDI MFC):
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.x, 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, L"0", 1);
pDC->SelectObject(oldfont);
}
void CIDEView::OnInitialUpdate() {
CScrollView::OnInitialUpdate();
SetScrollSizes(MM_TEXT, {linewidth, 128*lineheight});
}
BOOL CIDEView::OnEraseBkgnd(CDC *pDC) {
return TRUE;
}
CScrollView
class 是具有滚动功能的视图。您几乎可以像 CView
一样使用它(即在 OnDraw()
成员中绘图),只是您必须考虑可能的滚动。
GetClientRect()
函数returns可见客户区,返回的坐标不是相对于视图原点,而是相对于window原点,即left
和 top
成员始终为 0。 OnDraw()
函数中的 CDC
参数虽然是相对于逻辑视图原点的,因此需要进行调整。
至于您的代码,您不需要使用 OnEraseBkgnd()
函数,因为您在 OnDraw()
中这样做了。您只填充 window 的可见部分,但这样非常好。所以最好去掉。此外,传递给 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, pos.x, pos.y);
pDC->FillSolidRect(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, i * 54, L"0", 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
;
- 我发布的代码适用于循环中的
<
条件,您不需要 <=
。计算一下,你会发现这是正确的。
我的英语并不完美。我正在使用 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);
}
我无法上传图片和代码作为评论,所以我必须编辑原问题。
还有一个小bug。原代码(MDI MFC):
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.x, 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, L"0", 1);
pDC->SelectObject(oldfont);
}
void CIDEView::OnInitialUpdate() {
CScrollView::OnInitialUpdate();
SetScrollSizes(MM_TEXT, {linewidth, 128*lineheight});
}
BOOL CIDEView::OnEraseBkgnd(CDC *pDC) {
return TRUE;
}
CScrollView
class 是具有滚动功能的视图。您几乎可以像 CView
一样使用它(即在 OnDraw()
成员中绘图),只是您必须考虑可能的滚动。
GetClientRect()
函数returns可见客户区,返回的坐标不是相对于视图原点,而是相对于window原点,即left
和 top
成员始终为 0。 OnDraw()
函数中的 CDC
参数虽然是相对于逻辑视图原点的,因此需要进行调整。
至于您的代码,您不需要使用 OnEraseBkgnd()
函数,因为您在 OnDraw()
中这样做了。您只填充 window 的可见部分,但这样非常好。所以最好去掉。此外,传递给 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, pos.x, pos.y);
pDC->FillSolidRect(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, i * 54, L"0", 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
; - 我发布的代码适用于循环中的
<
条件,您不需要<=
。计算一下,你会发现这是正确的。