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原点,即lefttop 成员始终为 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;
  • 我发布的代码适用于循环中的 < 条件,您不需要 <=。计算一下,你会发现这是正确的。