本文共 11386 字,大约阅读时间需要 37 分钟。
用户点击窗口的关闭按钮后,Win32系统会向当前的Frame对象发送WM_CLOSE
消息,此时会进入到Frame的wxFrame::MSWWindowProc
函数进行处理:
WXLRESULT wxFrame::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam){ switch ( message ) { case WM_CLOSE: // if we can't close, tell the system that we processed the // message - otherwise it would close us processed = !Close(); break;...
Close
方法是由wxWindowBase
类提供的,调用过程如下:
wxEVT_CLOSE_WINDOW
消息,传递当前的windowIDbool wxWindowBase::Close(bool force){ wxCloseEvent event(wxEVT_CLOSE_WINDOW, m_windowId); event.SetEventObject(this); event.SetCanVeto(!force); // return false if window wasn't closed because the application vetoed the // close event return HandleWindowEvent(event) && !event.GetVeto();}
继续跟踪wxEVT_CLOSE_WINDOW
消息的处理,前面我们有介绍单文档的Frame窗口的继承关系如下,我们可以根据此继承关系找消息处理函数:
wxDocParentFrame -> wxDocParentFrameBase (wxDocParentFrameAny < wxFrame > ) -- wxFrame & wxDocParentFrameAnyBase
我们可以找到wxDocParentFrameAny
模板类中的Create
方法中注册了wxEVT_CLOSE_WINDOW
消息的处理函数是wxDocParentFrameAny<>::OnCloseWindow
:
// wxDocParentFrameAny < >bool Create(wxDocManager *manager, ...){ m_docManager = manager; if ( !BaseFrame::Create(frame, id, title, pos, size, style, name) ) return false; this->Connect(wxID_EXIT, wxEVT_MENU, wxCommandEventHandler(wxDocParentFrameAny::OnExit)); this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxDocParentFrameAny::OnCloseWindow)); return true;}
wxDocParentFrameAny<>::OnCloseWindow
会调用m_docManager
的Clear方法来实现关闭,只要是MVC类,则必然存在文档管理类。
// wxDocParentFrameAny < >void OnCloseWindow(wxCloseEvent& event){ if ( m_docManager && !m_docManager->Clear(!event.CanVeto()) ) { // The user decided not to close finally, abort. event.Veto(); } else { // Just skip the event, base class handler will destroy the window. event.Skip(); }}
接着我们看下文档管理类的wxDocManager::Clear
方法实现:
CloseDocuments
关闭所有文档,这步很关键,关闭文档的关键路径;wxDocManager
管理多个文档模板,此时需要将所有的文档模板对象全部删除。bool wxDocManager::Clear(bool force){ if (!CloseDocuments(force)) return false; m_currentView = NULL; wxList::compatibility_iterator node = m_templates.GetFirst(); while (node) { wxDocTemplate *templ = (wxDocTemplate*) node->GetData(); wxList::compatibility_iterator next = node->GetNext(); delete templ; node = next; } return true;}
跟踪关键路径wxDocManager::CloseDocuments
,这里的操作步骤就是找到此文档管理器管理的所有文档,然后调用CloseDocument
方法关闭文档:
bool wxDocManager::CloseDocuments(bool force){ wxList::compatibility_iterator node = m_docs.GetFirst(); while (node) { wxDocument *doc = (wxDocument *)node->GetData(); wxList::compatibility_iterator next = node->GetNext(); if (!CloseDocument(doc, force)) return false; node = next; } return true;}
执行关闭文档操作,几个步骤:
Close
方法执行关闭;DeleteAllViews
方法关闭此文档关联的View;bool wxDocManager::CloseDocument(wxDocument* doc, bool force){ if ( !doc->Close() && !force ) return false; doc->DeleteAllViews(); if (m_docs.Member(doc)) delete doc; return true;}
wxDocManager
调用文档类的wxDocument::Close
方法执行关闭,这个方法的默认实现是关闭此文档类关联的所有子文档对象,然后调用用户可以实现的OnCloseDocument
方法。
OnCloseDocument
的默认实现仅仅是进行清理而已。
bool wxDocument::Close(){ if ( !OnSaveModified() ) return false; DocsList::const_iterator it = m_childDocuments.begin(); for ( DocsList::const_iterator end = m_childDocuments.end(); it != end; ++it ) { if ( !(*it)->OnSaveModified() ) { return false; } } while ( !m_childDocuments.empty() ) { wxDocument * const childDoc = m_childDocuments.front(); if ( !childDoc->Close() ) { wxFAIL_MSG( "Closing the child document unexpectedly failed " "after its OnSaveModified() returned true" ); } childDoc->DeleteAllViews(); } return OnCloseDocument();}bool wxDocument::OnCloseDocument(){ // Tell all views that we're about to close NotifyClosing(); DeleteContents(); Modify(false); return true;}
调用wxDocument::DeleteAllViews
删除此文档关联的所有视图,对于一个View来说有两个操作步骤:
wxView::Close
方法关闭view;bool wxDocument::DeleteAllViews(){ wxDocManager* manager = GetDocumentManager(); // first check if all views agree to be closed const wxList::iterator end = m_documentViews.end(); for ( wxList::iterator i = m_documentViews.begin(); i != end; ++i ) { wxView *view = (wxView *)*i; if ( !view->Close() ) return false; } // all views agreed to close, now do close them if ( m_documentViews.empty() ) { if ( manager && manager->GetDocuments().Member(this) ) delete this; } else // have views { for ( ;; ) { wxView *view = (wxView *)*m_documentViews.begin(); bool isLastOne = m_documentViews.size() == 1; delete view; if ( isLastOne ) break; } } return true;}
View类的Close
方法比较简单,就是调用用户的OnClose
方法,当然OnClose
也有默认实现,wxView::OnClose
的默认实现就是调用wxDocument::Close
,前文已经描述过,不赘述:
bool wxView::Close(bool deleteWindow){ return OnClose(deleteWindow);}bool wxView::OnClose(bool WXUNUSED(deleteWindow)){ return GetDocument() ? GetDocument()->Close() : true;}
我们还需关注delete view;
这行语句,这行实现了view与doc类的脱离关系,如下,最终调用m_viewDocument->RemoveView
实现了将自己从doc中移除:
wxView::~wxView(){ if (m_viewDocument && GetDocumentManager()) GetDocumentManager()->ActivateView(this, false); if ( m_docChildFrame && m_docChildFrame->GetView() == this ) { m_docChildFrame->SetView(NULL); m_docChildFrame->GetWindow()->Destroy(); } if ( m_viewDocument ) m_viewDocument->RemoveView(this);}
跟踪wxDocument::RemoveView
,可以看到此时会从doc的m_documentViews
列表中删除此view,然后再调用OnChangedViewList
进行后处理,OnChangedViewList
检查到当前doc不关联任何view的时候,会发起自己的删除delete this
。
bool wxDocument::RemoveView(wxView *view){ (void)m_documentViews.DeleteObject(view); OnChangedViewList(); return true;}void wxDocument::OnChangedViewList(){ if ( m_documentViews.empty() && OnSaveModified() ) delete this;}
在前文中我们看到Doc对象的关闭是动态注册的处理函数,由于最后在wxDocParentFrameAny<>::OnCloseWindow
方法中调用event.Skip()
,这样这个消息还会继续传递处理,接下来继续查找静态消息表,我们可以看到在wxTopLevelWindowBase
中有如下定义:
BEGIN_EVENT_TABLE(wxTopLevelWindowBase, wxWindow) EVT_CLOSE(wxTopLevelWindowBase::OnCloseWindow) EVT_SIZE(wxTopLevelWindowBase::OnSize)END_EVENT_TABLE()
那么此时会继续静态消息处理表的处理,对应的处理入口消息为wxTopLevelWindowBase::OnCloseWindow
:
wxPendingDelete
队列中,这样下次IDLE的时候会一并删除;void wxTopLevelWindowBase::OnCloseWindow(wxCloseEvent& WXUNUSED(event)){ Destroy();}bool wxTopLevelWindowBase::Destroy(){ if ( wxWindow* parent = GetParent() ) { if ( parent->IsBeingDeleted() ) return wxNonOwnedWindow::Destroy(); } if ( !wxPendingDelete.Member(this) ) wxPendingDelete.Append(this); for ( wxWindowList::const_iterator i = wxTopLevelWindows.begin(), end = wxTopLevelWindows.end(); i != end; ++i ) { wxTopLevelWindow * const win = static_cast(*i); if ( win != this && win->IsShown() ) { Hide(); break; } } return true;}
再回过头来,我们看看APP类的清理,APP类的启动过程可以参考启动代码部分,这里回到调用点wxEntryReal
,程序执行完成后会从wxTheApp->OnRun()
中返回,也就是退出wxTRY的作用域,我们来重点看下CallOnExit
的作用:
在定义CallOnExit
类的时候一起创建了一个对象callOnExit
,此对象位于栈上,那么退出wxTRY的作用域时必然会调用到此对象的析构函数,参考定义,我们看到析构函数最终调用的是wxTheApp->OnExit()
。
这样通过临时对象的析构函数调用了APP类的OnExit
fangf
int wxEntryReal(int& argc, wxChar **argv){ wxInitializer initializer(argc, argv); if ( !initializer.IsOk() ) { return -1; } wxTRY { if ( !wxTheApp->CallOnInit() ) { return -1; } // ensure that OnExit() is called if OnInit() had succeeded class CallOnExit { public: ~CallOnExit() { wxTheApp->OnExit(); } } callOnExit; WX_SUPPRESS_UNUSED_WARN(callOnExit); return wxTheApp->OnRun(); } wxCATCH_ALL( wxTheApp->OnUnhandledException(); return -1; )}
多文档窗口的父窗口用于容纳一个或者多个子窗口,关闭过程同wxFrame的正常关闭过程。
如何通知到子窗口?注意看Destroy。。。
一般使用中,我们会独立关闭多文档APP的子窗口,操作过程是用户点击了子窗口Frame的关闭按钮,消息的产生过程与单文档窗口类似,我们可以找到wxDocChildFrameAny<>
类中Create函数中创建了对wxEVT_CLOSE_WINDOW
消息的动态绑定wxDocChildFrameAny::OnCloseWindow
。
bool Create(wxDocument *doc, ...){ if ( !wxDocChildFrameAnyBase::Create(doc, view, this) ) return false; if ( !BaseClass::Create(parent, id, title, pos, size, style, name) ) return false; this->Connect(wxEVT_ACTIVATE, wxActivateEventHandler(wxDocChildFrameAny::OnActivate)); this->Connect(wxEVT_CLOSE_WINDOW, wxCloseEventHandler(wxDocChildFrameAny::OnCloseWindow)); return true;}
OnCloseWindow
方法的调用比较简单,我们逐个看实现:
// wxDocChildFrameAny < > void OnCloseWindow(wxCloseEvent& event){ if ( CloseView(event) ) Destroy(); //else: vetoed}
wxDocChildFrameAnyBase::CloseView
用于关闭view,会调用View的Close
方法,前文我们知道View在关闭的时候会再次调用doc的Close
方法,这样就可以保证视图和文档对象都关闭了。
关闭完成后执行删除视图就可以了,在删除视图的析构函数中会再次调用文档的RemoveView
,当文档对象检查到自己没有关联的视图对象时,文档会删除自己。
到此,视图和文档就已经删除了。
bool wxDocChildFrameAnyBase::CloseView(wxCloseEvent& event){ if ( m_childView ) { if ( !m_childView->Close(false) && event.CanVeto() ) { event.Veto(); return false; } m_childView->Activate(false); m_childView->SetDocChildFrame(NULL); wxDELETE(m_childView); } m_childDocument = NULL; return true;}
继续我们看下wxDocChildFrameAny<>::Destroy
方法,这个方法会调用父类实现的Destroy
,调用Destroy
方法后就会销毁窗口,同普通窗口销毁。
// wxDocChildFrameAny < > virtual bool Destroy(){ // FIXME: why exactly do we do this? to avoid activation events during // destructions maybe? m_childView = NULL; return BaseClass::Destroy();}
我们再关注下子窗口是何时与父窗口脱离关系的呢?找到wxMDIChildFrame
的析构函数:
GetMDIParent()->RemoveMDIChild
方法恢复父类原来的菜单;MSWDestroyWindow
将自己与父类窗口脱离关系wxMDIChildFrame::~wxMDIChildFrame(){ // if we hadn't been created, there is nothing to destroy if ( !m_hWnd ) return; GetMDIParent()->RemoveMDIChild(this); m_frameToolBar = NULL; m_frameStatusBar = NULL; DestroyChildren(); MDIRemoveWindowMenu(NULL, m_hMenu); MSWDestroyWindow();}
这样子窗口删除后,父窗口会继续存活。
App处于空闲状态时会调用wxAppConsoleBase::ProcessIdle
方法,这个方法进而调用DeletePendingObjects
用于清理待删除的窗口对象。
wxPendingDelete
中取得所有的待删除的对象,delete掉。 bool wxAppConsoleBase::ProcessIdle(){ // synthesize an idle event and check if more of them are needed wxIdleEvent event; event.SetEventObject(this); ProcessEvent(event);// Garbage collect all objects previously scheduled for destruction. DeletePendingObjects(); return event.MoreRequested();}void wxAppConsoleBase::DeletePendingObjects(){ wxList::compatibility_iterator node = wxPendingDelete.GetFirst(); while (node) { wxObject *obj = node->GetData(); if ( wxPendingDelete.Member(obj) ) wxPendingDelete.Erase(node); delete obj; node = wxPendingDelete.GetFirst(); }}
下面对关闭时用户可以捕捉的时机做个总结,用户可以通过下面的方法来截获此消息。
类型 | 用户重载的方法 |
---|---|
wxApp | virtual int OnExit () |
wxDocument | virtual bool OnCloseDocument () |
wxView | virtual bool OnClose (bool deleteWindow) |
wxFrame | virtual bool Destroy () // 重载时必须要调用父类的方法 |
从上面的分析,我们看到删除view之前,我们需要先调用wxView::Close
方法,此时会调用用户的
OnClose
方法,然后再delete掉。 void deleteView(wxView *view){ view->Close(); delete view}
转载地址:http://toyfz.baihongyu.com/