MFC多文档-视结构学习笔记(学习《深入浅出MFC》)
MFC 多文档-视图的 数据结构:
1.CWinApp 的成员变量 CDocManager* m_pDocManager 管理模板
2.CDocManager 的成员变量 CPtrList m_templateList 维护结构模板 CMultiDocTemplate new的对象指针
3.CDocMultiTemplate 的成员变量 CRuntimeClass* m_pDocClass,m_pViewClass,m_pFrameClass 用来动态创建,另外还有一个资源ID对应资源文件中的内容
4.CDocument 的成员变量CDocTemplate * m_pDocTemplate,回指其DocumentTemplate;成员变量CPtrList m_viewList,同时维护一组Views。
CFrameWnd 的成员变量CView * m_pViewActive,指向当前活动的View。
CView 的成员变量CDocument * m_pDocument,指向对应的Document。
NOTE:从数据结构看,文档对应的多个View,没有Frame链表,如果要多个Frame只能手工增加。
新建过程调用关系:
1.模板在WinApp::InitInistance 中创建,并加到CWinApp::m_pDocManager->m_templateList 中
2.新建时,WinApp::OnFileNew()
3.调用WinApp中 CDocManager::OnFileNew();
4.调用 CMultiDocTemplate::OnDocumentFile( LPCTSTR lpszFileName ); 需要创建的对象就是成员变量CRuntimeClass指针
CMultiDocTemplate::OnDocumentFile中具体的新建过程(参数 lpszFileName用来区分是新建还是打开):
1.CreateNewDocument()新建一个Doc对象,并加到CMultiDocTemplate的成员变量 CPtrList m_docList中, 用到RuntimeClass* m_pDocClass
2.CreateNewFrame(pDocument, NULL);新建一个Frame对象,把Doc的对象指针传给Frame
2.1在新建Frame对象后,LoadFrame函数创建Frame窗口,在Frame窗口的WM_CREATE消息响应中创建(Helper), Client,在CLient中CreateView(CCreateContext*pContext, UINT nID)创建View,
用到RuntimeClass* m_pFrameClass,m_pViewClass,资源ID,还有之前的pDocument
一个文档对应多个视窗的情况
新建视窗3元组合,是WinApp的模板对象的OnDocumentFile( LPCTSTR lpszFileName );用不同的模板对象就能创建不同的窗口,每次调用都会新建一个Doc对象。也就是不同源的窗口。
如果要建立同源的多个视窗,也就是对应同一个DOC,则在创建另一个Frame时,首先要获取活动对象的doc指针。用doc指针做参数创建另一个Frame。
1.CMDIChildWnd* pActiveChild = ((CMainFrame*)m_pMainWnd)->MDIGetActive(); //得到活动窗口
2.CDocument* pDocument = pActiveChild->GetActiveDocument(); //得到doc指针
3.CMultiDocTemplate* pDocTemplate = ((CMT1App*)AfxGetApp())->m_pDocTemplate2; //若不在CWinApp
4.CFrameWnd* pFrame = pDocTemplate->CreateNewFrame(pDocument, pActiveChild); //创建窗口
5.pDocTemplate->InitialUpdateFrame(pFrame, pDocument); //初始视图显示窗口??
主要的程序段:MT1.cpp
// MT1.cpp : Defines the class behaviors for the application. // #include "pch.h" #include "framework.h" #include "afxwinappex.h" #include "afxdialogex.h" #include "MT1.h" #include "MainFrm.h" #include "ChildFrm.h" #include "MT1Doc.h" #include "MT1View.h" #include "CMyView2.h" #include "CMyChildFrm2.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CMT1App BEGIN_MESSAGE_MAP(CMT1App, CWinApp) ON_COMMAND(ID_APP_ABOUT, &CMT1App::OnAppAbout) // Standard file based document commands //ON_COMMAND(ID_FILE_NEW, &CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, &CWinApp::OnFileOpen) // Standard print setup command ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinApp::OnFilePrintSetup) ON_COMMAND(ID_FILE_NEW, &CMT1App::OnFileNew) END_MESSAGE_MAP() // CMT1App construction CMT1App::CMT1App() noexcept { // support Restart Manager m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_ALL_ASPECTS; #ifdef _MANAGED // If the application is built using Common Language Runtime support (/clr): // 1) This additional setting is needed for Restart Manager support to work properly. // 2) In your project, you must add a reference to System.Windows.Forms in order to build. System::Windows::Forms::Application::SetUnhandledExceptionMode(System::Windows::Forms::UnhandledExceptionMode::ThrowException); #endif // TODO: replace application ID string below with unique ID string; recommended // format for string is CompanyName.ProductName.SubProduct.VersionInformation SetAppID(_T("MT1.AppID.NoVersion")); // TODO: add construction code here, // Place all significant initialization in InitInstance } // The one and only CMT1App object CMT1App theApp; // CMT1App initialization BOOL CMT1App::InitInstance() { // InitCommonControlsEx() is required on Windows XP if an application // manifest specifies use of ComCtl32.dll version 6 or later to enable // visual styles. Otherwise, any window creation will fail. INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); // Set this to include all the common control classes you want to use // in your application. InitCtrls.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&InitCtrls); CWinApp::InitInstance(); // Initialize OLE libraries if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; } AfxEnableControlContainer(); EnableTaskbarInteraction(FALSE); // AfxInitRichEdit2() is required to use RichEdit control // AfxInitRichEdit2(); // Standard initialization // If you are not using these features and wish to reduce the size // of your final executable, you should remove from the following // the specific initialization routines you do not need // Change the registry key under which our settings are stored // TODO: You should modify this string to be something appropriate // such as the name of your company or organization SetRegistryKey(_T("Local AppWizard-Generated Applications")); LoadStdProfileSettings(4); // Load standard INI file options (including MRU) // Register the application's document templates. Document templates // serve as the connection between documents, frame windows and views CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate(IDR_MT1TYPE, RUNTIME_CLASS(CMT1Doc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CMT1View)); if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); m_pDocTemplate1 = pDocTemplate; pDocTemplate = new CMultiDocTemplate(IDR_MT1TYPE, RUNTIME_CLASS(CMT1Doc), RUNTIME_CLASS(CMyChildFrm2), // custom MDI child frame RUNTIME_CLASS(CMyView2)); if (!pDocTemplate) return FALSE; //AddDocTemplate(pDocTemplate); m_pDocTemplate2 = pDocTemplate; // create main MDI Frame window CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME)) { delete pMainFrame; return FALSE; } m_pMainWnd = pMainFrame; // Parse command line for standard shell commands, DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); // Dispatch commands specified on the command line. Will return FALSE if // app was launched with /RegServer, /Register, /Unregserver or /Unregister. if (!ProcessShellCommand(cmdInfo)) return FALSE; // The main window has been initialized, so show and update it pMainFrame->ShowWindow(m_nCmdShow); pMainFrame->UpdateWindow(); return TRUE; } int CMT1App::ExitInstance() { //TODO: handle additional resources you may have added AfxOleTerm(FALSE); return CWinApp::ExitInstance(); } // CMT1App message handlers // CAboutDlg dialog used for App About class CAboutDlg : public CDialogEx { public: CAboutDlg() noexcept; // Dialog Data #ifdef AFX_DESIGN_TIME enum { IDD = IDD_ABOUTBOX }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support // Implementation protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() noexcept : CDialogEx(IDD_ABOUTBOX) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // App command to run the dialog void CMT1App::OnAppAbout() { CAboutDlg aboutDlg; aboutDlg.DoModal(); } // CMT1App message handlers void CMT1App::OnFileNew() { // TODO: Add your command handler code here //CWinApp::OnFileNew(); m_pDocTemplate1->OpenDocumentFile(NULL); //会创建一个新的 DOC对象,放在m_docList中 //创建不同源的窗口 //m_pDocTemplate2->OpenDocumentFile(NULL); //会创建一个新的 DOC对象,放在m_docList中
//创建同源的窗口
CMDIChildWnd* pActiveChild = ((CMainFrame*)m_pMainWnd)->MDIGetActive(); //得到活动窗口
CDocument* pDocument = pActiveChild->GetActiveDocument(); //得到doc指针
//CMultiDocTemplate* pDocTemplate = ((CMT1App*)AfxGetApp())->m_pDocTemplate2; //若不在CWinApp
CFrameWnd* pFrame = m_pDocTemplate2->CreateNewFrame(pDocument, pActiveChild); //创建窗口
m_pDocTemplate2->InitialUpdateFrame(pFrame, pDocument); //初始视图显示窗口??
}