上文相当详细的解释了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; }
但是,如果使用了拆分,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,一说是驱动问题。暂时无法验证。
如果有解决方案,希望读者留言告之。