MFC文档视图结构内幕_编程

来源:互联网 发布:spss如何合并两个数据 编辑:程序博客网 时间:2024/06/06 03:38
/* 1.回顾"initinstance函数" */ /////////////////////////////////////////////这里让我们在回顾一下cmywinapp::initinstance()函数,并将里面与文档视图结构有关的代码深入探讨一下:bool cmyapp::initinstance()//只列出了与文档视图结构相关的源代码{//... //文档模板将用作文档、框架窗口和视图之间的连接cmultidoctemplate* pdoctemplate;pdoctemplate = new cmultidoctemplate(idr_mytype,runtime_class(cmydoc),runtime_class(cchildframe), // 自定义 mdi 子框架runtime_class(cmyview));adddoctemplate(pdoctemplate);// 创建主 mdi 框架窗口cmainframe* pmainframe = new cmainframe;if (!pmainframe->loadframe(idr_mainframe))return false;m_pmainwnd = pmainframe;// 仅当具有后缀时才调用 dragacceptfiles// 在 mdi 应用程序中,这应在设置 m_pmainwnd 之后立即发生// 分析标准外壳命令、dde、打开文件操作的命令行ccommandlineinfo cmdinfo;parsecommandline(cmdinfo);// 调度在命令行中指定的命令。如果// 用 /regserver、/register、/unregserver 或 /unregister 启动应用程序,则返回 false。if (!processshellcommand(cmdinfo))return false;// 主窗口已初始化,因此显示它并对其进行更新pmainframe->showwindow(m_ncmdshow);pmainframe->updatewindow();return true;} //////////////////////////////////////////// /* 2.初始化文档模板 */ ////////////////////////////////////////////分析以下代码:cmultidoctemplate* pdoctemplate;pdoctemplate = new cmultidoctemplate(idr_mytype,runtime_class(cmydoc),runtime_class(cchildframe), // 自定义 mdi 子框架runtime_class(cmyview));应用程序首先实例化一个cmultidoctemplate对象,此过程也是对cmultidoctemplate类数据成员的初始化过程。按调用次序我列出了以下源代码:注释1: cdoctemplate构造函数定义在:../visual studio.net/vc7/atlmfc/src/mfc/doctempl.cppcdoctemplate::cdoctemplate(uint nidresource, cruntimeclass* pdocclass,cruntimeclass* pframeclass, cruntimeclass* pviewclass)//部分源代码{assert_valid_idr(nidresource);assert(pdocclass == null ||pdocclass->isderivedfrom(runtime_class(cdocument)));assert(pframeclass == null ||pframeclass->isderivedfrom(runtime_class(cframewnd)));assert(pviewclass == null ||pviewclass->isderivedfrom(runtime_class(cview)));m_nidresource = nidresource;...//m_pdocclass = pdocclass;m_pframeclass = pframeclass;m_pviewclass = pviewclass;m_poleframeclass = null;m_poleviewclass = null;...//}以上为cmultidoctemplate类的基类cdoctemplate构造函数的部分源代码,该函数初始化了四个重要的成员m_nidresource,m_pdocclass,m_pframeclass和m_pviewclass。注释2: cmultidoctemplate构造函数定义在:../visual studio.net/vc7/atlmfc/src/mfc/docmulti.cppcmultidoctemplate::cmultidoctemplate(uint nidresource, cruntimeclass* pdocclass,cruntimeclass* pframeclass, cruntimeclass* pviewclass): cdoctemplate(nidresource, pdocclass, pframeclass, pviewclass){assert(m_doclist.isempty());m_hmenushared = null;m_hacceltable = null;m_nuntitledcount = 0; // start at 1// load resources in constructor if not statically allocatedif (!cdocmanager::bstaticinit)loadtemplate();}看完以上代码后,来回过头看一看initinstance函数将什么参数值传给了cmultidoctemplate的构造函数。原来是一些runtime_class宏。以下是runtime_class宏的定义:注释3: 以下的宏定义在:../visual studio.net/vc7/atlmfc/include/afx.h中#define runtime_class(class_name) _runtime_class(class_name)#define _runtime_class(class_name) ((cruntimeclass*)(&class_name::class##class_name))源代码中这样作是为了将cmydoc,cchildframe,cmyview各类中的static cruntimeclass class##class_name地址赋予cmultidoctemplate类的各cruntimeclass*指针成员m_pdocclass,m_pframeclass和m_pviewclass,这位以后的动态创建document/frame/view"三口组"打下了基础。 /////////////////////////////////////////// /* 3.文档模板列队 */ ///////////////////////////////////////////文档模板初始化结束后,initinstance函数调用了cwinapp::adddoctemplate(pdoctemplate)函数,其主要目的是将以初始化后的那个文档模板加入到文档模板链表中,并由cdocmanager类对象进行管理。以下操作就是为了完成此工作。注释1: 以下函数定义在:../visual studio.net/vc7/atlmfc/src/mfc/appui2.cppvoid cwinapp::adddoctemplate(cdoctemplate* ptemplate)//将cmultidoctemplate* pdoctemplate{ //传给ptemplateif (m_pdocmanager == null)m_pdocmanager = new cdocmanager;m_pdocmanager->adddoctemplate(ptemplate);}注释2: 以下函数定义在:../visual studio.net/vc7/atlmfc/src/mfc/docmgr.cppcdocmanager::cdocmanager(){}//目前是一个空函数;void cdocmanager::adddoctemplate(cdoctemplate* ptemplate)//部分源代码{if (ptemplate == null){...//}else{assert_valid(ptemplate);assert(m_templatelist.find(ptemplate, null) == null);// must not be in listptemplate->loadtemplate();m_templatelist.addtail(ptemplate);//cptrlist m_templatelist is a member //of cdocmanager}} /////////////////////////////////////////////// /* 4.创建程序主框架窗口 */ ///////////////////////////////////////////////应用程序实例化了一个cmainframe类对象,并调用loadframe函数加载窗口资源创建主框架窗口。以下是创建主框架窗口的流程。创建窗口的主要代码是:pmainframe->loadframe(idr_mainframe);loadframe函数是mfc包装了窗口创建过程的函数,在后面动态创建child窗口时,它还将披挂上阵(但稍有不同)。下面是它的源代码,让我们仔细分析一下:注释1: 以下函数定义在:../visual studio.net/vc7/atlmfc/src/mfc/winmdi.cppbool cmdiframewnd::loadframe(uint nidresource, dword dwdefaultstyle,cwnd* pparentwnd, ccreatecontext* pcontext) {if (!cframewnd::loadframe(nidresource, dwdefaultstyle,pparentwnd, pcontext))return false;// save menu to use when no active mdi child window is presentassert(m_hwnd != null);m_hmenudefault = ::getmenu(m_hwnd);if (m_hmenudefault == null)trace(traceappmsg, 0, "warning: cmdiframewnd without a default menu./n");return true;}cmdiframewnd::loadframe调用了其基类cframewnd的loadframe,并将参数原封不动的传给它。注释2: 以下函数定义在:../visual studio.net/vc7/atlmfc/src/mfc/winfrm.cppbool cframewnd::loadframe(uint nidresource, dword dwdefaultstyle,cwnd* pparentwnd, ccreatecontext* pcontext) //部分源代码{...//cstring strfullstring;if (strfullstring.loadstring(nidresource))afxextractsubstring(m_strtitle, strfullstring, 0); // first sub-stringverify(afxdeferregisterclass(afx_wndframeorview_reg));// attempt to create the windowlpctstr lpszclass = geticonwndclass(dwdefaultstyle, nidresource);cstring strtitle = m_strtitle;if (!create(lpszclass, strtitle, dwdefaultstyle, rectdefault,pparentwnd, makeintresource(nidresource), 0l, pcontext)){return false; // will self destruct on failure normally}...//if (pcontext == null) // send initial updatesendmessagetodescendants(wm_initialupdate, 0, 0, true, true);return true;} ////////////////////////////////////////////////////// /* 4.1注册应用程序主框架窗口类 */ //////////////////////////////////////////////////////在传统的win32api编程中,创建窗口一般步骤是定义窗口类,注册窗口类,并调用::createwindow函数来创建。前面说过loadframe函数封装了mfc创建窗口的过程,那么也就是说loadframe函数将负责定义窗口类,注册窗口类等琐碎工作。下面我们就通过挖掘源代码来看看loadframe函数是如何完成这些工作的。loadframe首先调用afxdeferregisterclass(afx_wndframeorview_reg),注释1: 以下宏定义在:../visual studio.net/vc7/atlmfc/src/mfc/afximpl.h#define afxdeferregisterclass(fclass) afxenddeferregisterclass(fclass)注释2: 以下函数定义在:../visual studio.net/vc7/atlmfc/src/mfc/wincore.cppbool afxapi afxenddeferregisterclass(long ftoregister)//部分源代码{// mask off all classes that are already registeredafx_module_state* pmodulestate = afxgetmodulestate();ftoregister &= ~pmodulestate->m_fregisteredclasses;if (ftoregister == 0)return true;long fregisteredclasses = 0;// common initializationwndclass wndcls;memset(&wndcls, 0, sizeof(wndclass)); // start with null defaultswndcls.lpfnwndproc = defwindowproc;wndcls.hinstance = afxgetinstancehandle();wndcls.hcursor = afxdata.hcurarrow;initcommoncontrolsex init;init.dwsize = sizeof(init);// work to register classes as specified by ftoregister, populate fregisteredclasses as we goif (ftoregister & afx_wnd_reg){// child windows - no brush, no icon, safest default class styleswndcls.style = cs_dblclks | cs_hredraw | cs_vredraw;wndcls.lpszclassname = _afxwnd;if (afxregisterclass(&wndcls))fregisteredclasses |= afx_wnd_reg;}...//if (ftoregister & afx_wndmdiframe_reg){// mdi frame window (also used for splitter window)wndcls.style = cs_dblclks;wndcls.hbrbackground = null;if (_afxregisterwithicon(&wndcls, _afxwndmdiframe, afx_idi_std_mdiframe))fregisteredclasses |= afx_wndmdiframe_reg;}if (ftoregister & afx_wndframeorview_reg){// sdi frame or mdi child windows or views - normal colorswndcls.style = cs_dblclks | cs_hredraw | cs_vredraw;wndcls.hbrbackground = (hbrush) (color_window + 1);if (_afxregisterwithicon(&wndcls, _afxwndframeorview, afx_idi_std_frame))fregisteredclasses |= afx_wndframeorview_reg;} ...//// must have registered at least as mamy classes as requestedreturn (ftoregister & fregisteredclasses) == ftoregister;}mfc预定义了若干个“窗口类模板”,比如"afx_wndmdiframe_reg","afx_wndframeorview_reg"等,mfc在loadframe函数中调用afxenddeferregisterclass函数为你的应用程序预注册了适当的窗口类。本例中预注册的窗口类为afx_wndframeorview_reg。(注意是预注册,如果你在后面更改了createstruct结构的域成员,mfc还会根据你的更改重新为你的应用程序正式注册新的窗口类,稍候会有详细叙述)预注册完窗口类,mfc将判断你是否想更改窗口类的各参数。若你更改了,则mfc会重新注册新类;否则源预注册的窗口类就将成为正式的窗口类。下面我们来看看mfc的判断过程:此判断过程由geticonwndclass开始lpctstr lpszclass = geticonwndclass(dwdefaultstyle, nidresource);注释3: 以下函数定义在:../visual studio.net/vc7/atlmfc/src/mfc/winfrm.cpplpctstr cframewnd::geticonwndclass(dword dwdefaultstyle, uint nidresource)//部分源代码{...//hicon hicon = ::loadicon(hinst, makeintresource(nidresource));if (hicon != null){createstruct cs;memset(&cs, 0, sizeof(createstruct));cs.style = dwdefaultstyle;precreatewindow(cs);// will fill lpszclassname with default wndclass name// ignore instance handle from precreatewindow.wndclass wndcls;if (cs.lpszclass != null &&getclassinfo(afxgetinstancehandle(), cs.lpszclass, &wndcls) &&wndcls.hicon != hicon){// register a very similar wndclassreturn afxregisterwndclass(wndcls.style,wndcls.hcursor, wndcls.hbrbackground, hicon);}}return null; // just use the default}geticonwndclass函数将调用cmainframe::precreatewindow(createstruct& cs)来看看应用程序是否修改了createstruct结构的域成员。cmainframe::precreatewindow调用 cmdiframewnd::precreatewindow(createstruct& cs),后者的代码如下:bool cmdiframewnd::precreatewindow(createstruct& cs)//in winmdi.cpp{if (cs.lpszclass == null){verify(afxdeferregisterclass(afx_wndmdiframe_reg));cs.lpszclass = _afxwndmdiframe;}return true;}mfc将为应用程序注册afx_wndmdiframe_reg预定义窗口类,并设置cs.lpszclass = _afxwndmdiframe。在应用程序的代码中我更改了cs结构:bool cmainframe::precreatewindow(createstruct& cs){if( !cmdiframewnd::precreatewindow(cs) )return false;// todo: 在此处通过修改 createstruct cs 来修改窗口类或// 样式cs.dwexstyle=~ws_ex_clientedge;return true;} cmainframe::precreatewindow返回后,geticonwndclass函数调用getclassinfo函数重新收集cs信息(此时的信息已是更改后的了),并调用afxregisterwndclass函数重新注册该窗口类(此窗口类为该应用程序的正式窗口类)。到此为止窗口类注册完毕,以后程序还会调用一次cmainframe::precreatewindow,不过那此只是"过门不入"而已。 ///////////////////////////////////////////////// /* 4.2主框架窗口创建开始 */ /////////////////////////////////////////////////开始进入创建框架窗口的实质阶段。看loadframe函数做了什么?原来它调用了create函数。注释1: 以下函数定义在:../visual studio.net/vc7/atlmfc/src/mfc/winfrm.cppbool cframewnd::create(lpctstr lpszclassname,lpctstr lpszwindowname,dword dwstyle,const rect& rect,cwnd* pparentwnd,lpctstr lpszmenuname,dword dwexstyle,ccreatecontext* pcontext){hmenu hmenu = null;if (lpszmenuname != null){// load in a menu that will get destroyed when window gets destroyedhinstance hinst = afxfindresourcehandle(lpszmenuname, rt_menu);if ((hmenu = ::loadmenu(hinst, lpszmenuname)) == null){trace(traceappmsg, 0, "warning: failed to load menu for cframewnd./n");postncdestroy(); // perhaps delete the c++ objectreturn false;}}m_strtitle = lpszwindowname; // save title for laterif (!createex(dwexstyle, lpszclassname, lpszwindowname, dwstyle,rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,pparentwnd->getsafehwnd(), hmenu, (lpvoid)pcontext)){trace(traceappmsg, 0, "warning: failed to create cframewnd./n");if (hmenu != null)destroymenu(hmenu);return false;}return true;}简单地说cframewnd::create函数调用了基类的cwnd::createex;注释2: 以下函数定义在:../visual studio.net/vc7/atlmfc/src/mfc/wincore.cppbool cwnd::createex(dword dwexstyle, lpctstr lpszclassname,lpctstr lpszwindowname, dword dwstyle,int x, int y, int nwidth, int nheight,hwnd hwndparent, hmenu nidorhmenu, lpvoid lpparam)//部分源代码{// allow modification of several common create parameterscreatestruct cs;cs.dwexstyle = dwexstyle;cs.lpszclass = lpszclassname;cs.lpszname = lpszwindowname;cs.style = dwstyle;cs.x = x;cs.y = y;cs.cx = nwidth;cs.cy = nheight;cs.hwndparent = hwndparent;cs.hmenu = nidorhmenu;cs.hinstance = afxgetinstancehandle();cs.lpcreateparams = lpparam;if (!precreatewindow(cs)){postncdestroy();return false;}afxhookwindowcreate(this);hwnd hwnd = ::createwindowex(cs.dwexstyle, cs.lpszclass,cs.lpszname, cs.style, cs.x, cs.y, cs.cx, cs.cy,cs.hwndparent, cs.hmenu, cs.hinstance, cs.lpcreateparams);...//return true;}bool cmainframe::precreatewindow(createstruct& cs){if( !cmdiframewnd::precreatewindow(cs) )return false;// todo: 在此处通过修改 createstruct cs 来修改窗口类或// 样式cs.dwexstyle=~ws_ex_clientedge;return true;}bool cmdiframewnd::precreatewindow(createstruct& cs){if (cs.lpszclass == null){verify(afxdeferregisterclass(afx_wndmdiframe_reg));cs.lpszclass = _afxwndmdiframe;}return true;}cwnd::createex调用了cmainframe::precreatewindow,但此次afxdeferregisterclass将不会被调用。也就是我上面所说的“过门不入”。cwnd::createex函数还调用了afxhookwindowcreate(this);后者是干什么的呢?其实它与消息映射和命令传递有关. cwnd::createex调用win32api ::createwindowex函数(传统的win32api程序员一定不陌生这个函数),就这样主框架窗口创建结束。 ////////////////////////////////////////////// /* 5.标准外壳命令解析 */ ///////////////////////////////////////////////mfc向导制作的标准mdi应用程序启动时,应用程序会自动启动一个子窗口框架(实际上是一套文档模板),这是为何呢?下面我将详细讲解一下这个创建过程.其实这一过程也是在cmywinapp::initinstance()函数中完成的,看看下面代码:ccommandlineinfo cmdinfo;parsecommandline(cmdinfo);if (!processshellcommand(cmdinfo))return false;函数首先实例化一个ccommandlineinfo类对象cmdinfo,让我们看看ccommandlineinfo是个什么东东? //in afxwin.hclass ccommandlineinfo : public cobject//部分源代码{public:// sets default valuesccommandlineinfo();...//bool m_bshowsplash;bool m_brunembedded;bool m_brunautomated;enum { filenew, fileopen, fileprint, fileprintto, filedde, appregister,appunregister, filenothing = -1 } m_nshellcommand;// not valid for filenewcstring m_strfilename;// valid only for fileprinttocstring m_strprintername;cstring m_strdrivername;cstring m_strportname;~ccommandlineinfo();// implementation...//}; 再让我们来看看它的构造函数的实现://in appcore.cppccommandlineinfo::ccommandlineinfo(){m_bshowsplash = true;m_brunembedded = false;m_brunautomated = false;m_nshellcommand = filenew;}m_nshellcommand = filenew;这一句对我们最重要;至于cwinapp::parsecommandline我想用mfc文档中的一句话来解释:call this member function to parse the command line and send the parameters, one at a time, to ccommandlineinfo::parseparam.下面我们来看看外壳命令解析的主角:cwinapp::processshellcommand//in appui2.cpp //dde and shellexecute supportbool cwinapp::processshellcommand(ccommandlineinfo& rcmdinfo)//部分源代码{bool bresult = true;switch (rcmdinfo.m_nshellcommand){case ccommandlineinfo::filenew:if (!afxgetapp()->oncmdmsg(id_file_new, 0, null, null))onfilenew();if (m_pmainwnd == null)bresult = false;break;// if weve been asked to open a file, call opendocumentfile()case ccommandlineinfo::fileopen:if (!opendocumentfile(rcmdinfo.m_strfilename))bresult = false;break;case ccommandlineinfo::fileprintto:case ccommandlineinfo::fileprint:...//case ccommandlineinfo::filedde:...//case ccommandlineinfo::appregister:...//case ccommandlineinfo::appunregister:...//}return bresult;}挖掘源代码的确是了解mfc运行内幕的最好手段,大家一看源代码便知道如之何了。ccommandlineinfo构造函数中m_nshellcommand = filenew;所以在processshellcommand中对应的代码自然就一目了然了:cwinapp::onfilenew()被调用了。 ////////////////////////////////////////////////// /* 6.一套文档/视图即将诞生 */ //////////////////////////////////////////////////上文说cwinapp::onfilenew()被调用了,那么就让我来看看其代码吧!//in appdlg.cppvoid cwinapp::onfilenew(){if (m_pdocmanager != null)m_pdocmanager->onfilenew();}//in docmgr.cppvoid cdocmanager::onfilenew()//部分源代码{...//cdoctemplate* ptemplate = (cdoctemplate*)m_templatelist.gethead();if (m_templatelist.getcount() > 1){// more than one document template to choose from// bring up dialog prompting usercnewtypedlg dlg(&m_templatelist);int_ptr nid = dlg.domodal();if (nid == idok)ptemplate = dlg.m_pselectedtemplate;elsereturn; // none - cancel operation}assert(ptemplate != null);assert_kindof(cdoctemplate, ptemplate);ptemplate->opendocumentfile(null);// if returns null, the user has already been alerted}//in docmulti.cppcdocument* cmultidoctemplate::opendocumentfile(lpctstr lpszpathname,bool bmakevisible)//部分源代码{cdocument* pdocument = createnewdocument();...//bool bautodelete = pdocument->m_bautodelete;pdocument->m_bautodelete = false; // dont destroy if something goes wrongcframewnd* pframe = createnewframe(pdocument, null);pdocument->m_bautodelete = bautodelete;...//if (lpszpathname == null){// create a new document - with default document namesetdefaulttitle(pdocument);// avoid creating temporary compound file when starting up invisibleif (!bmakevisible)pdocument->m_bembedded = true;if (!pdocument->onnewdocument()){// user has be alerted to what failed in onnewdocumenttrace(traceappmsg, 0, "cdocument::onnewdocument returned false./n");pframe->destroywindow();return null;}// it worked, now bump untitled countm_nuntitledcount++;}else{// open an existing documentcwaitcursor wait;if (!pdocument->onopendocument(lpszpathname)){// user has be alerted to what failed in onopendocumenttrace(traceappmsg, 0, "cdocument::onopendocument returned false./n");pframe->destroywindow();return null;}pdocument->setpathname(lpszpathname);}initialupdateframe(pframe, pdocument, bmakevisible);return pdocument;} ////////////////////////////////////////////// /* 6.1.子文档动态生成 */ //////////////////////////////////////////////cmultidoctemplate::opendocumentfile调用了createnewdocument(),这就是子文档动态生成的主函数。//in doctempl.cppcdocument* cdoctemplate::createnewdocument()//部分源代码{// default implementation constructs one from cruntimeclass...//cdocument* pdocument = (cdocument*)m_pdocclass->createobject();...//adddocument(pdocument);//将动态生成的文档对象的指针加入到应用程序的文档列表中return pdocument;}cdocument* pdocument = (cdocument*)m_pdocclass->createobject();这一句就是动态产生的核心,它借助于cruntimeclass动态生成一个cdocument对象。 ////////////////////////////////////////////////// /* 6.2.子窗口框架动态生成 */ /////////////////////////////////////////////////cmultidoctemplate::opendocumentfile调用了createnewframe,这就是子窗口框架动态生成的主函数。// default frame creationcframewnd* cdoctemplate::createnewframe(cdocument* pdoc, cframewnd* pother)//部分源代码{if (pdoc != null)assert_valid(pdoc);// create a frame wired to the specified documentassert(m_nidresource != 0); // must have a resource id to load fromccreatecontext context;context.m_pcurrentframe = pother;context.m_pcurrentdoc = pdoc;context.m_pnewviewclass = m_pviewclass;context.m_pnewdoctemplate = this;...//cframewnd* pframe = (cframewnd*)m_pframeclass->createobject();if (pframe == null){trace(traceappmsg, 0, "warning: dynamic create of frame %hs failed./n",m_pframeclass->m_lpszclassname);return null;}assert_kindof(cframewnd, pframe);...//// create new from resourceif (!pframe->loadframe(m_nidresource,ws_overlappedwindow | fws_addtotitle, // default frame stylesnull, &context)){trace(traceappmsg, 0, "warning: cdoctemplate couldnt create a frame./n");// frame will be deleted in postncdestroy cleanupreturn null;}// it worked !return pframe;}cframewnd* pframe = (cframewnd*)m_pframeclass->createobject();这一句就是动态产生的核心,它借助于cruntimeclass动态生成一个cdocument对象。之后函数调用loadframe来创建子窗口。其过程与创建主框架窗口的过程大致相同,但也有一些不同的地方,下面我就将说说这点不同。 ////////////////////////////////////////////// /* 6.3.子视图动态生成 */ //////////////////////////////////////////////瞪大眼睛仔细察看opendocumentfile的源代码,疑惑了,"怎么没有类似cview* pview =createnewview();的代码?","那么子视图是如何生成的呢"下面我就为你详细解释一下吧!其实子视图动态生成函数被放到另一个地方了。让我们详细来看看吧。其实,关键还是在loadframe,但与创建主窗口框架的那个loadframe不同的是传进了一个不同的参数&context,你回过头看看主窗口框架的那个loadframe,调用它时使用了默认参数,而那个默认参数值为null,下面看看ccreatecontext 结构。//in afxext.hstruct ccreatecontext // creation information structure// all fields are optional and may be null{// for creating new viewscruntimeclass* m_pnewviewclass; // runtime class of view to create or nullcdocument* m_pcurrentdoc;// for creating mdi children (cmdichildwnd::loadframe)cdoctemplate* m_pnewdoctemplate;// for sharing view/frame state from the original view/framecview* m_plastview;cframewnd* m_pcurrentframe;// implementationccreatecontext();};而在cdoctemplate::createnewframe中初始化了该结构如下:ccreatecontext context;context.m_pcurrentframe = pother;context.m_pcurrentdoc = pdoc;context.m_pnewviewclass = m_pviewclass;context.m_pnewdoctemplate = this;context.m_pnewviewclass = m_pviewclass;//关键的成员下面看看这个创建的具体过程:loadframe(...,&context)-->cframewnd::create(...,&context)--> cwnd::createex(...,&context)-->::createwindowex::createwindowex api函数将产生wm_create消息,并将&context传递之,cmainframe::oncreate将响应消息,并引起一系列的函数调用,看下面://in mainfrm.cppint cmainframe::oncreate(lpcreatestruct lpcreatestruct){if (cmdiframewnd::oncreate(lpcreatestruct) == -1)return -1;...//return 0;}// in winfrm.cppint cframewnd::oncreate(lpcreatestruct lpcs){ccreatecontext* pcontext = (ccreatecontext*)lpcs->lpcreateparams;return oncreatehelper(lpcs, pcontext);}// in winfrm.cppint cframewnd::oncreatehelper(lpcreatestruct lpcs, ccreatecontext* pcontext)//部分源代码{if (cwnd::oncreate(lpcs) == -1)return -1;// create special children firstif (!oncreateclient(lpcs, pcontext)){trace(traceappmsg, 0, "failed to create client pane/view for frame./n");return -1;}...//return 0; // create ok}// in winfrm.cppbool cframewnd::oncreateclient(lpcreatestruct, ccreatecontext* pcontext){// default create client will create a view if asked for itif (pcontext != null && pcontext->m_pnewviewclass != null){if (createview(pcontext, afx_idw_pane_first) == null)return false;}return true;}// in winfrm.cppcwnd* cframewnd::createview(ccreatecontext* pcontext, uint nid)//部分源代码{...//// note: can be a cwnd with postncdestroy self cleanupcwnd* pview = (cwnd*)pcontext->m_pnewviewclass->createobject();if (pview == null){trace(traceappmsg, 0, "warning: dynamic create of view type %hs failed./n",pcontext->m_pnewviewclass->m_lpszclassname);return null;}assert_kindof(cwnd, pview);// views are always created with a border!if (!pview->create(null, null, afx_ws_default_view,crect(0,0,0,0), this, nid, pcontext)){trace(traceappmsg, 0, "warning: could not create view for frame./n");return null; // cant continue without a view}...//return pview;}cwnd* pview = (cwnd*)pcontext->m_pnewviewclass->createobject();核心函数终于出现了。子视图动态生成完毕。 ///////////////////////////////////////// /* 7.收尾工作 */ /////////////////////////////////////////至此,一套完整的document/childframe/view结构生成,此“三口组”共属同一套文档模板,如果你要定义另一套不同的文档模档需再定义另一组不同“三口组”(childframe可以使用相同的)。并调用adddoctemplate将该文档模板加入到应用程序的文档模板列表。比如:cmultidoctemplate* potherdoctemplate;potherdoctemplate = new cmultidoctemplate(idr_myothertype,runtime_class(cmyotherdoc),runtime_class(cchildframe), // 自定义 mdi 子框架runtime_class(cmyotherview));adddoctemplate(potherdoctemplate);“三口组”生成后程序调用showwindow,updatewindow将应用程序的主窗口展现在你眼前。注释:当你在file菜单中选择new或在工具栏中单击“新建”时,应用程序将选择当前默认的文档模板并以它为基础动态生成 document/childframe/view“三口组”,其生成过程与我上述讲的一般不二。
原创粉丝点击