C++ 为什么';OnDraw就足够了(在MFC中,Scribble教程)

C++ 为什么';OnDraw就足够了(在MFC中,Scribble教程),c++,mfc,C++,Mfc,我正在制作Scribble教程来学习MFC,MouseMove事件中有以下代码: void CScribbleView::OnMouseMove(UINT nFlags, CPoint point) { // Mouse movement is interesting in the Scribble application // only if the user is currently drawing a new stroke by // dragging the ca

我正在制作Scribble教程来学习MFC,MouseMove事件中有以下代码:

void CScribbleView::OnMouseMove(UINT nFlags, CPoint point)
{
    // Mouse movement is interesting in the Scribble application
    // only if the user is currently drawing a new stroke by
    // dragging the captured mouse.

    if( GetCapture( ) != this )
        return;        // If this window (view) didn't capture the
    // mouse, the user isn't drawing in this window.

    // Add the new point to the current stroke
    m_pStrokeCur->m_pointArray.Add(point);

    CClientDC dc( this );

    // Draw a line from the previous detected point in the mouse
    // drag to the current point.
    CPen* pOldPen =
        dc.SelectObject( GetDocument( )->GetCurrentPen( ) );
           dc.MoveTo( m_ptPrev );
           dc.LineTo( point );
           dc.SelectObject( pOldPen );

    m_ptPrev = point;

    CView::OnMouseMove(nFlags, point);
}
这是笔划的绘图函数(由视图的OnDraw调用):

boolcstroke::DrawStroke(CDC*pDC)
{
CPen-penStroke;
if(!penStroke.CreatePen(PS_SOLID,m_nPenWidth,RGB(0,0,0)))
返回FALSE;
CPen*pOldPen=pDC->SelectObject(&penStroke);
pDC->MoveTo(m_pointArray[0]);
对于(int i=1;iLineTo(m_pointArray[i]);
}
pDC->选择对象(pOldPen);
返回TRUE;
}
我已经检查过,在每次帧更新时都会调用此函数。但是,如果我取消鼠标移动事件上的绘图,它将不会发生任何事情,即使我正在注册所有笔划并且正在调用draw stroke函数。只有当我对窗口进行更改(如最大化)时,才会出现


有人叫它,为什么它不画在窗户上?我在这里试图找出MFC的内部工作原理,它不是一个bug或任何东西。

当您的全部或部分窗口过期(称为“无效”)时,消息循环将得到两条消息:WM_ERASEBKGND,然后WM_PAINT。Windows应用程序绘制的典型方式是在WM_ERASEBKGND和WM_PAINT处理程序中绘制所有内容。(我不是MFC专家,但我相信WM_PAINT对应于MFC中的OnDraw。)

因此,处理这个问题的正常方法是鼠标移动处理程序记录笔划(正如它所做的那样),然后将窗口标记为无效。这最终会导致划清界线。但是,可能会有轻微的延迟,并可能导致闪烁。这种延迟在现代计算机上可能是微不足道的(但Scribble已经过时了)。有多种方法来处理闪烁

Scribble作者似乎选择了通过直接在鼠标移动处理程序中绘制线条来处理延迟和闪烁,而不是使窗口无效并让OnDraw稍后执行

失效是关键。您已从OnMouseMove中删除了图形,因此不会在那里绘制线。但是没有什么可以告诉Windows窗口内容现在已经过期(无效),因此它不会收到WM_PAINT消息,也不会调用OnDraw。(稍后,当您执行诸如调整窗口大小或最大化窗口之类的操作时,确实会使其无效,并且会调用OnDraw并突然显示该行。)


如果要从OnMouseMove中删除图形,则必须使用invalidate调用替换它。这将告诉Windows需要重新绘制窗口。

我在这里猜测,但这可能与Windows的底层GDI(图形设备接口)有关。一般来说,为了节省处理能力,它不希望像在3D应用程序中那样,每一帧都重新绘制所有内容,只是屏幕上已更改的区域。您在MouseMove中而不是DrawStroke中所做的某些操作可能是将曲面标记为需要在windows上重新绘制。。。不是mfc。在调用OnPaint之前,是否考虑到背景已被擦除?在MouseMove函数中绘制内存位图,禁用背景绘制以避免闪烁,并在调用OnPaint时将内存位图绘制到DC。要强制调用OnPaint,请使用InValidate、UpdateWindow…我正在编写教程,所以我不太了解它是如何工作的,我希望以前有人这样做过。此外,在位图上绘制也不是一个解决方案,因为我不希望任何事情以不同的方式发生,我只想了解它为什么以这种方式工作。我认为内森的见解可能是正确的,但我对GDIs的工作原理知之甚少。我知道一个设备是自动传递给OnDraw的(无论是谁调用OnDraw,我都不知道它是哪个类),而在MouseMove函数中,我是通过CClientDC(view)获取它的。下一步我会测试的。好吧,没关系,我发现了问题所在。OnDraw实际上不会被调用,除非您在其上放置一个窗口或对窗口进行更改,如调整其大小。我认为它被调用是因为当我添加一个断点时,VS窗口将叠加在MFC窗口上,当我回来时,需要重新绘制,使其再次调用OnDraw,然后继续。当我从OnDraw上取下断点时,不会调用它,因此会产生混乱。我可以通过设置两个窗口看到这一点,而不是一个窗口在另一个窗口之上。
BOOL CStroke::DrawStroke( CDC* pDC )
{
    CPen penStroke;
    if( !penStroke.CreatePen(PS_SOLID, m_nPenWidth, RGB(0,0,0)))
        return FALSE;
    CPen* pOldPen = pDC->SelectObject( &penStroke );
    pDC->MoveTo( m_pointArray[0] );
    for( int i=1; i < m_pointArray.GetSize(); i++ )
    {
        pDC->LineTo( m_pointArray[i] );
    }
    pDC->SelectObject( pOldPen );
    return TRUE;
}