如何调用 OnEraseBkgnd() 在另一个函数中重新绘制窗口? (MFC)

Posted

技术标签:

【中文标题】如何调用 OnEraseBkgnd() 在另一个函数中重新绘制窗口? (MFC)【英文标题】:How to call OnEraseBkgnd() to repaint the window in another function? (MFC) 【发布时间】:2016-12-03 02:06:52 【问题描述】:

我正在尝试调用OnEraseBkgnd() 在另一个函数中重新绘制窗口。

比如下面的代码:

...

CBitmap Background;
BITMAP bm;
CDC dcMemory;
int width, height;

...

BOOL CSplashDlg::OnInitDialog()

    CDialog::OnInitDialog();

    CDC* dc;

    Background.LoadBitmap(IDB_COVER);   //Load Bitmap
    Background.GetBitmap(&bm);      //Load Bitmap into handle

    width = 0;
    height = 0;

    while(width < 300)
    
        width += 10;
        height += 10;
        OnEraseBkgnd(dc);       //<--- Here I want to call OnEraseBkgnd()

        Sleep(5);           //Delay 5 millisecond
    

    return TRUE;




BOOL CSplashDlg::OnEraseBkgnd(CDC* pDC)

    ///////////////////////////////////
      Invalidate();             //I don't know where I should put this function
    ///////////////////////////////////

    dcMemory.CreateCompatibleDC(pDC);
    CBitmap *pOldbmp = dcMemory.SelectObject(&Background);

    pDC->SetStretchBltMode(HALFTONE);

    pDC->StretchBlt(0, 0, width, height, &dcMemory, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);

    return TRUE;

在上面的例子中,我想在OnInitDialog() 内部执行时调用OnEraseBkgnd() 来重新绘制窗口。我搜索了互联网,它总是说使用Invalidate();RedrawWindow(); 重新绘制它,它还会调用OnEraseBkgnd() 函数。问题是:我应该如何使用Invalidate();RedrawWindow();?我应该把这些功能放在哪里?

我已经把这两个函数放在了任何地方,但它仍然不起作用。

编辑:

我已经修改过了。虽然现在Invalidate()UpdateWindow() 成功调用了OnEraseBkgnd() 函数。但是我发现了另一个问题:为什么我使用Invalidate()UpdateWindow() 重新绘制它时StretchBlt 不起作用,但FillSolidRect 起作用?

...

BOOL CMainDlg::OnInitDialog()

    CSplashDlg Frame;

    Frame.width = 0;
    Frame.height = 0;

    while(Frame.width <= 300)
    
        Frame.width += 10;
        Frame.height += 10;

        Frame.Invalidate();     //<---Here I use both Invalidate() and UpdateWindow()
        Frame.UpdateWindow();       //<---Here I use both Invalidate() and UpdateWindow()

        Sleep(5);           //Delay 5 millisecond
    

    CDialog::OnInitDialog();

    return TRUE;


BOOL CSplashDlg::OnInitDialog()

    CDialog::OnInitDialog();

    width = 0;
    height = 0;

    Background.LoadBitmap(IDB_COVER);   //Load Bitmap
    Background.GetBitmap(&bm);      //Load Bitmap into handle


    return TRUE;




BOOL CSplashDlg::OnEraseBkgnd(CDC* pDC)

    dcMemory.CreateCompatibleDC(pDC);
    CBitmap *pOldbmp = dcMemory.SelectObject(&Background);

    ///////////////////////////////////////////////////////////////
    pDC->SetStretchBltMode(HALFTONE);
    pDC->StretchBlt(0, 0, width, height, &dcMemory, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);        //It doesn't work when using this one (StretchBlt)
    ///////////////////////////////////////////////////////////////
    pDC->FillSolidRect(0, 0, width, height, RGB(255,0,0));                      //But it works when using this one (FillSolidRect)
    ///////////////////////////////////////////////////////////////

    return TRUE;


...

【问题讨论】:

***.com/questions/2183649/… OnInitDialog 返回之前,对话框不可见。你可以在OnInitDialog 中做的是创建一个5 毫秒的定时器SetTimer(1, 5, 0),将ON_WM_TIMER 添加到消息映射中,然后在OnTimer(UINT_PTR) 中调用Invalidate()。画在OnPaint,而不是OnEraseBkgnd 在您的第二次编辑中,一切都正确,除了Frame-&gt;Invalidate Windows 消息被阻止后,它不会更新窗口,因为系统仍在处理另一条消息 (WM_INITDIALOG)。您在它之后调用UpdateWindow,希望说服Windows 处理该消息,但它仍然没有这样做。唯一的选择是在两者之间插入一个消息循环(使用PeekMessage),以处理消息队列中的当前消息,但您必须小心。或使用WM_TIMER 执行此操作 【参考方案1】:

您永远不应该调用OnEraseBkgnd( )OnPaint( ) 之类的名称。当需要绘图时,MFC 会调用这些。你的工作是处理OnEraseBkgnd( ) 的绘制需求。

Invalidate( ) 导致 MFC 更新绘图区域,然后它将调用 OnEraseBkgnd( )OnPaint( )。如果您在OnPaint( )OnEraseBkgnd( ) 中使用Invalidate( ),则程序可能会挂起,因为它会导致重绘窗口的无限循环。

您可以在OnInitDialog( ) 中致电Invalidate( ),但可能没有必要。

所以从OnEraseBkgnd( ) 中取出Invalidate,不要在OnInitDialog( ) 中调用OnEraseBkgnd( ) 并从那里开始。

你也必须有

ON_WM_PAINT( ) ON_WM_ERASEBKGND( )

在 MFC 调用的消息映射中。

注意: 我不相信重新发明***。大部分都是以前做过的。我没有在我的项目中使用闪屏,但如果需要,我会从这里开始:Splash Screen C++ Class using MFC。

我没有下载和查看代码,但有四颗星,它应该是一个不错的起点。 MFC 不是一夜之间就能学会的。当我开始时,我阅读书籍和大量搜索来学习。您只是无法猜测基础架构的工作原理。希望这会有所帮助...

【讨论】:

我已将Invalidate() 放入OnInitDialog() 并将ON_WM_ERASEBKGND() 添加到消息映射中,但它仍然没有重绘任何内容。 @BananaCode 调用Invalidate 只是标记要重新绘制的窗口。要查看更改,您需要在失效后调用UpdateWindow,不要失效并调用RedrawWindow,或者在失效后运行消息循环。 我发现了另一个问题:为什么当我使用Invalidate()UpdateWindow()StretchBlt 不起作用但FillSolidRect 起作用?代码在更新的内容中。 那么这告诉您问题可能与位图有关。您确定 IDB_COVER 是有效资源吗?我看不到其他任何东西,您的代码应该可以工作。我“相当”确定我没有错过什么。

以上是关于如何调用 OnEraseBkgnd() 在另一个函数中重新绘制窗口? (MFC)的主要内容,如果未能解决你的问题,请参考以下文章

学习:单选框和多选框

VC绘制控件如何防止闪烁

MFC双缓存技术代码

如何在另一个 AppDomain 中调用方法

如何在另一个类中调用一个类的 main() 方法?

如何调用在另一个函数中创建的对象