上文相当详细的解释了MFC中使用OpenGL的基本的流程。并给出了实现无闪烁的步骤。
现在给出第一个问题:如果你在MFC生成向导中选择了“拆分窗口”,你会发现即使重载了OnEraseBkgnd,也无法解决闪烁问题!
笔者经过排查,发现问题出现在CMainFrame中MFC帮你生成的如下代码:
//在MainFrm.h中:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
//在MainFrm.cpp中:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
CCreateContext* pContext)
{
return m_wndSplitter.Create(this,
2, 2, // TODO: 调整行数和列数
CSize(10, 10), // TODO: 调整最小窗格大小
pContext);
}
也就是说MFC为实现拆分窗口帮你重载了OnCreateClient函数。我们知道MainFrm继承自CFrameWnd。我们看一下CFrameWnd的OnCreateClient做了什么:
BOOL CFrameWnd::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)
{
// default create client will create a view if asked for it
if (pContext != NULL && pContext->m_pNewViewClass != NULL)
{
if (CreateView(pContext, AFX_IDW_PANE_FIRST) == NULL)
return FALSE;
}
return TRUE;
}
这里就创建了我们初始化OpenGL的视图类CxxxView了。
但是,如果使用了拆分,MFC会自动添加一个类型为CSplitterWnd的对象m_wndSplitter,而你的CxxxView类则仅仅是CSplitterWnd内部的视图罢了。
在改变窗口大小时,CSplitterWnd首先响应ON_WM_SIZE,然后在其响应函数中让CxxxView进一步处理。
CSplitterWnd的窗口大小要略大于其内部的CxxxView。因此CSplitterWnd自身的刷新会影响CxxxView的刷新而而导致闪烁。
那么解决方法,要么不使用拆分,这个绝对没有问题。
如果必须要拆分窗口,那么可能需要派生自己的CSplitterWnd类,修改其行为.
下一个问题,你可能遇到窗口不刷新,只有改变窗口大小的情况才刷新。
这是因为你的CxxxView只重载了OnDraw的原因。OnDraw是如何被调用的?
参见*\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\viewcore.cpp
/
// CView drawing support
void CView::OnPaint()
{
// standard paint routine
CPaintDC dc(this);
OnPrepareDC(&dc);
OnDraw(&dc);
}
响应ON_WM_PAINT消息的是CView,他调用了你重载的OnDraw。正因为这里的CPaintDC dc(this);导致了窗口不能持续刷新。
要知道更详细的原因,请参见http://peipengshuai.blog.163.com/blog/static/1901226620123169431072/
你只需要知道,CPaintDC会从刷新消息队列中移除ON_WM_PAINT即可。移除后,如果窗口没有发生改变,就不会自动刷新。
因此,你需要做的很简单。自己响应你的CxxxView类的ON_WM_PAINT,并不要在里面使用CPaintDC dc(this);(注释掉)即可。
再下一个问题:最大化窗口后,窗口某些区域(如工具栏)无法刷新,留下空白。
这个问题尚未得到完美解决方案。参考http://topic.csdn.net/t/20020220/16/534304.html,一说是驱动问题。暂时无法验证。
如果有解决方案,希望读者留言告之。