深入浅出MFC文档/视图架构之基本概念
///////////////////////////////////////////////////////////////////////////// // CDocTemplate manager object class CDocManager : public CObject { DECLARE_DYNAMIC(CDocManager) public: // Constructor CDocManager(); //Document functions virtual void AddDocTemplate(CDocTemplate* pTemplate); virtual POSITION GetFirstDocTemplatePosition() const; virtual CDocTemplate* GetNextDocTemplate(POSITION& pos) const; virtual void RegisterShellFileTypes(BOOL bCompat); void UnregisterShellFileTypes(); virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName); // open named file virtual BOOL SaveAllModified(); // save before exit virtual void CloseAllDocuments(BOOL bEndSession); // close documents before exiting virtual int GetOpenDocumentCount(); // helper for standard commdlg dialogs virtual BOOL DoPromptFileName(CString& fileName, UINT nIDSTitle, DWORD lFlags, BOOL bOpenFileDialog, CDocTemplate* pTemplate); //Commands // Advanced: process async DDE request virtual BOOL OnDDECommand(LPTSTR lpszCommand); virtual void OnFileNew(); virtual void OnFileOpen(); // Implementation protected: CPtrList m_templateList; int GetDocumentCount(); // helper to count number of total documents public: static CPtrList* pStaticList; // for static CDocTemplate objects static BOOL bStaticInit; // TRUE during static initialization static CDocManager* pStaticDocManager; // for static CDocTemplate objects public: virtual ~CDocManager(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif }; |
class CPtrList : public CObject { DECLARE_DYNAMIC(CPtrList) protected: struct CNode { CNode* pNext; CNode* pPrev; void* data; }; public: // Construction CPtrList(int nBlockSize = 10); // Attributes (head and tail) // count of elements int GetCount() const; BOOL IsEmpty() const; // peek at head or tail void*& GetHead(); void* GetHead() const; void*& GetTail(); void* GetTail() const; // Operations // get head or tail (and remove it) - don't call on empty list! void* RemoveHead(); void* RemoveTail(); // add before head or after tail POSITION AddHead(void* newElement); POSITION AddTail(void* newElement); // add another list of elements before head or after tail void AddHead(CPtrList* pNewList); void AddTail(CPtrList* pNewList); // remove all elements void RemoveAll(); // iteration POSITION GetHeadPosition() const; POSITION GetTailPosition() const; void*& GetNext(POSITION& rPosition); // return *Position++ void* GetNext(POSITION& rPosition) const; // return *Position++ void*& GetPrev(POSITION& rPosition); // return *Position-- void* GetPrev(POSITION& rPosition) const; // return *Position-- // getting/modifying an element at a given position void*& GetAt(POSITION position); void* GetAt(POSITION position) const; void SetAt(POSITION pos, void* newElement); void RemoveAt(POSITION position); // inserting before or after a given position POSITION InsertBefore(POSITION position, void* newElement); POSITION InsertAfter(POSITION position, void* newElement); // helper functions (note: O(n) speed) POSITION Find(void* searchValue, POSITION startAfter = NULL) const; // defaults to starting at the HEAD // return NULL if not found POSITION FindIndex(int nIndex) const; // get the 'nIndex'th element (may return NULL) // Implementation protected: CNode* m_pNodeHead; CNode* m_pNodeTail; int m_nCount; CNode* m_pNodeFree; struct CPlex* m_pBlocks; int m_nBlockSize; CNode* NewNode(CNode*, CNode*); void FreeNode(CNode*); public: ~CPtrList(); #ifdef _DEBUG void Dump(CDumpContext&) const; void AssertValid() const; #endif // local typedefs for class templates typedef void* BASE_TYPE; typedef void* BASE_ARG_TYPE; }; 很显然,CPtrList是对链表结构体 struct CNode { CNode* pNext; CNode* pPrev; void* data; }; |
virtual void AddDocTemplate(CDocTemplate* pTemplate); virtual POSITION GetFirstDocTemplatePosition() const; virtual CDocTemplate* GetNextDocTemplate(POSITION& pos) const; |
void CDocManager::AddDocTemplate(CDocTemplate* pTemplate) { if (pTemplate == NULL) { if (pStaticList != NULL) { POSITION pos = pStaticList->GetHeadPosition(); while (pos != NULL) { CDocTemplate* pTemplate = (CDocTemplate*)pStaticList->GetNext(pos); AddDocTemplate(pTemplate); } delete pStaticList; pStaticList = NULL; } bStaticInit = FALSE; } else { ASSERT_VALID(pTemplate); ASSERT(m_templateList.Find(pTemplate, NULL) == NULL);// must not be in list pTemplate->LoadTemplate(); m_templateList.AddTail(pTemplate); } } POSITION CDocManager::GetFirstDocTemplatePosition() const { return m_templateList.GetHeadPosition(); } CDocTemplate* CDocManager::GetNextDocTemplate(POSITION& pos) const { return (CDocTemplate*)m_templateList.GetNext(pos); } |
class CDocTemplate : public CCmdTarget { DECLARE_DYNAMIC(CDocTemplate) // Constructors protected: CDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass); public: virtual void LoadTemplate(); // Attributes public: // setup for OLE containers void SetContainerInfo(UINT nIDOleInPlaceContainer); // setup for OLE servers void SetServerInfo(UINT nIDOleEmbedding, UINT nIDOleInPlaceServer = 0, CRuntimeClass* pOleFrameClass = NULL, CRuntimeClass* pOleViewClass = NULL); // iterating over open documents virtual POSITION GetFirstDocPosition() const = 0; virtual CDocument* GetNextDoc(POSITION& rPos) const = 0; // Operations public: virtual void AddDocument(CDocument* pDoc); // must override virtual void RemoveDocument(CDocument* pDoc); // must override enum DocStringIndex { windowTitle, // default window title docName, // user visible name for default document fileNewName, // user visible name for FileNew // for file based documents: filterName, // user visible name for FileOpen filterExt, // user visible extension for FileOpen // for file based documents with Shell open support: regFileTypeId, // REGEDIT visible registered file type identifier regFileTypeName, // Shell visible registered file type name }; virtual BOOL GetDocString(CString& rString, enum DocStringIndex index) const; // get one of the info strings CFrameWnd* CreateOleFrame(CWnd* pParentWnd, CDocument* pDoc,BOOL bCreateView); // Overridables public: enum Confidence { noAttempt, maybeAttemptForeign, maybeAttemptNative, yesAttemptForeign, yesAttemptNative, yesAlreadyOpen }; virtual Confidence MatchDocType(LPCTSTR lpszPathName,CDocument*& rpDocMatch); virtual CDocument* CreateNewDocument(); virtual CFrameWnd* CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther); virtual void InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,BOOL bMakeVisible = TRUE); virtual BOOL SaveAllModified(); // for all documents virtual void CloseAllDocuments(BOOL bEndSession); virtual CDocument* OpenDocumentFile( LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE) = 0; // open named file // if lpszPathName == NULL => create new file with this type virtual void SetDefaultTitle(CDocument* pDocument) = 0; // Implementation public: BOOL m_bAutoDelete; virtual ~CDocTemplate(); // back pointer to OLE or other server (NULL if none or disabled) CObject* m_pAttachedFactory; // menu & accelerator resources for in-place container HMENU m_hMenuInPlace; HACCEL m_hAccelInPlace; // menu & accelerator resource for server editing embedding HMENU m_hMenuEmbedding; HACCEL m_hAccelEmbedding; // menu & accelerator resource for server editing in-place HMENU m_hMenuInPlaceServer; HACCEL m_hAccelInPlaceServer; #ifdef _DEBUG virtual void Dump(CDumpContext&) const; virtual void AssertValid() const; #endif virtual void OnIdle(); // for all documents virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo); protected: UINT m_nIDResource; // IDR_ for frame/menu/accel as well UINT m_nIDServerResource; // IDR_ for OLE inplace frame/menu/accel UINT m_nIDEmbeddingResource; // IDR_ for OLE open frame/menu/accel UINT m_nIDContainerResource; // IDR_ for container frame/menu/accel CRuntimeClass* m_pDocClass; // class for creating new documents CRuntimeClass* m_pFrameClass; // class for creating new frames CRuntimeClass* m_pViewClass; // class for creating new views CRuntimeClass* m_pOleFrameClass; // class for creating in-place frame CRuntimeClass* m_pOleViewClass; // class for creating in-place view CString m_strDocStrings; // '\n' separated names // The document names sub-strings are represented as _one_ string: // windowTitle\ndocName\n ... (see DocStringIndex enum) }; |
CDocTemplate::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_nIDServerResource = NULL; m_nIDEmbeddingResource = NULL; m_nIDContainerResource = NULL; m_pDocClass = pDocClass; m_pFrameClass = pFrameClass; m_pViewClass = pViewClass; m_pOleFrameClass = NULL; m_pOleViewClass = NULL; m_pAttachedFactory = NULL; m_hMenuInPlace = NULL; m_hAccelInPlace = NULL; m_hMenuEmbedding = NULL; m_hAccelEmbedding = NULL; m_hMenuInPlaceServer = NULL; m_hAccelInPlaceServer = NULL; // add to pStaticList if constructed as static instead of on heap if (CDocManager::bStaticInit) { m_bAutoDelete = FALSE; if (CDocManager::pStaticList == NULL) CDocManager::pStaticList = new CPtrList; if (CDocManager::pStaticDocManager == NULL) CDocManager::pStaticDocManager = new CDocManager; CDocManager::pStaticList->AddTail(this); } else { m_bAutoDelete = TRUE; // usually allocated on the heap LoadTemplate(); } } |
void CDocTemplate::AddDocument(CDocument* pDoc) { ASSERT_VALID(pDoc); ASSERT(pDoc->m_pDocTemplate == NULL); // no template attached yet pDoc->m_pDocTemplate = this; } void CDocTemplate::RemoveDocument(CDocument* pDoc) { ASSERT_VALID(pDoc); ASSERT(pDoc->m_pDocTemplate == this); // must be attached to us pDoc->m_pDocTemplate = NULL; } |
CDocument* CDocTemplate::CreateNewDocument() { // default implementation constructs one from CRuntimeClass if (m_pDocClass == NULL) { TRACE0("Error: you must override CDocTemplate::CreateNewDocument.\n"); ASSERT(FALSE); return NULL; } CDocument* pDocument = (CDocument*)m_pDocClass->CreateObject(); if (pDocument == NULL) { TRACE1("Warning: Dynamic create of document type %hs failed.\n",m_pDocClass->m_lpszClassName); return NULL; } ASSERT_KINDOF(CDocument, pDocument); AddDocument(pDocument); return pDocument; } |
CSingleDocTemplate::CSingleDocTemplate(UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass) : CDocTemplate(nIDResource, pDocClass, pFrameClass, pViewClass) { m_pOnlyDoc = NULL; } void CSingleDocTemplate::AddDocument(CDocument* pDoc) { ASSERT(m_pOnlyDoc == NULL); // one at a time please ASSERT_VALID(pDoc); CDocTemplate::AddDocument(pDoc); m_pOnlyDoc = pDoc; } void CSingleDocTemplate::RemoveDocument(CDocument* pDoc) { ASSERT(m_pOnlyDoc == pDoc); // must be this one ASSERT_VALID(pDoc); CDocTemplate::RemoveDocument(pDoc); m_pOnlyDoc = NULL; } |
// CMultiDocTemplate document management (a list of currently open documents) void CMultiDocTemplate::AddDocument(CDocument* pDoc) { ASSERT_VALID(pDoc); CDocTemplate::AddDocument(pDoc); ASSERT(m_docList.Find(pDoc, NULL) == NULL); // must not be in list m_docList.AddTail(pDoc); } void CMultiDocTemplate::RemoveDocument(CDocument* pDoc) { ASSERT_VALID(pDoc); CDocTemplate::RemoveDocument(pDoc); m_docList.RemoveAt(m_docList.Find(pDoc)); } |
POSITION CMultiDocTemplate::GetFirstDocPosition() const { return m_docList.GetHeadPosition(); } CDocument* CMultiDocTemplate::GetNextDoc(POSITION& rPos) const { return (CDocument*)m_docList.GetNext(rPos); } |
POSITION CSingleDocTemplate::GetFirstDocPosition() const { return (m_pOnlyDoc == NULL) ? NULL : BEFORE_START_POSITION; } CDocument* CSingleDocTemplate::GetNextDoc(POSITION& rPos) const { CDocument* pDoc = NULL; if (rPos == BEFORE_START_POSITION) { // first time through, return a real document ASSERT(m_pOnlyDoc != NULL); pDoc = m_pOnlyDoc; } rPos = NULL; // no more return pDoc; } |
BOOL CDocTemplate::SaveAllModified() { POSITION pos = GetFirstDocPosition(); while (pos != NULL) { CDocument* pDoc = GetNextDoc(pos); if (!pDoc->SaveModified()) return FALSE; } return TRUE; } void CDocTemplate::CloseAllDocuments(BOOL) { POSITION pos = GetFirstDocPosition(); while (pos != NULL) { CDocument* pDoc = GetNextDoc(pos); pDoc->OnCloseDocument(); } } |
///////////////////////////////////////////////////////////////////////////// // Default frame creation CFrameWnd* CDocTemplate::CreateNewFrame(CDocument* pDoc, CFrameWnd* pOther) { if (pDoc != NULL) ASSERT_VALID(pDoc); // create a frame wired to the specified document ASSERT(m_nIDResource != 0); // must have a resource ID to load from CCreateContext context; context.m_pCurrentFrame = pOther; context.m_pCurrentDoc = pDoc; context.m_pNewViewClass = m_pViewClass; context.m_pNewDocTemplate = this; if (m_pFrameClass == NULL) { TRACE0("Error: you must override CDocTemplate::CreateNewFrame.\n"); ASSERT(FALSE); return NULL; } CFrameWnd* pFrame = (CFrameWnd*)m_pFrameClass->CreateObject(); if (pFrame == NULL) { TRACE1("Warning: Dynamic create of frame %hs failed.\n",m_pFrameClass->m_lpszClassName); return NULL; } ASSERT_KINDOF(CFrameWnd, pFrame); if (context.m_pNewViewClass == NULL) TRACE0("Warning: creating frame with no default view.\n"); // create new from resource if (!pFrame->LoadFrame(m_nIDResource,WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, // default frame styles NULL, &context)) { TRACE0("Warning: CDocTemplate couldn't create a frame.\n"); // frame will be deleted in PostNcDestroy cleanup return NULL; } // it worked ! return pFrame; } void CDocTemplate::InitialUpdateFrame(CFrameWnd* pFrame, CDocument* pDoc,BOOL bMakeVisible) { // just delagate to implementation in CFrameWnd pFrame->InitialUpdateFrame(pDoc, bMakeVisible); } |
class CWinApp : public CWinThread { … CDocManager* m_pDocManager; // Running Operations - to be done on a running application // Dealing with document templates void AddDocTemplate(CDocTemplate* pTemplate); POSITION GetFirstDocTemplatePosition() const; CDocTemplate* GetNextDocTemplate(POSITION& pos) const; // Dealing with files virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName); // open named file void CloseAllDocuments(BOOL bEndSession); // close documents before exiting // Command Handlers protected: // map to the following for file new/open afx_msg void OnFileNew(); afx_msg void OnFileOpen(); int GetOpenDocumentCount(); … }; |
BOOL CSDIExampleApp::InitInstance() { … CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate(IDR_MAINFRAME,RUNTIME_CLASS(CSDIExampleDoc), RUNTIME_CLASS(CMainFrame), // main SDI frame window RUNTIME_CLASS(CSDIExampleView)); AddDocTemplate(pDocTemplate); … return TRUE; } BOOL CMDIExampleApp::InitInstance() { … CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate(IDR_MDIEXATYPE, RUNTIME_CLASS(CMDIExampleDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CMDIExampleView)); AddDocTemplate(pDocTemplate); … } |
///////////////////////////////////////////////////////////////////////////// // class CDocument is the main document data abstraction class CDocument : public CCmdTarget { DECLARE_DYNAMIC(CDocument) public: // Constructors CDocument(); // Attributes public: const CString& GetTitle() const; virtual void SetTitle(LPCTSTR lpszTitle); const CString& GetPathName() const; virtual void SetPathName(LPCTSTR lpszPathName, BOOL bAddToMRU = TRUE); CDocTemplate* GetDocTemplate() const; virtual BOOL IsModified(); virtual void SetModifiedFlag(BOOL bModified = TRUE); // Operations void AddView(CView* pView); void RemoveView(CView* pView); virtual POSITION GetFirstViewPosition() const; virtual CView* GetNextView(POSITION& rPosition) const; // Update Views (simple update - DAG only) void UpdateAllViews(CView* pSender, LPARAM lHint = 0L, CObject* pHint = NULL); // Overridables // Special notifications virtual void OnChangedViewList(); // after Add or Remove view virtual void DeleteContents(); // delete doc items etc // File helpers virtual BOOL OnNewDocument(); virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); virtual BOOL OnSaveDocument(LPCTSTR lpszPathName); virtual void OnCloseDocument(); virtual void ReportSaveLoadException(LPCTSTR lpszPathName, CException* e, BOOL bSaving, UINT nIDPDefault); virtual CFile* GetFile(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError); virtual void ReleaseFile(CFile* pFile, BOOL bAbort); // advanced overridables, closing down frame/doc, etc. virtual BOOL CanCloseFrame(CFrameWnd* pFrame); virtual BOOL SaveModified(); // return TRUE if ok to continue virtual void PreCloseFrame(CFrameWnd* pFrame); // Implementation protected: // default implementation CString m_strTitle; CString m_strPathName; CDocTemplate* m_pDocTemplate; CPtrList m_viewList; // list of views BOOL m_bModified; // changed since last saved public: BOOL m_bAutoDelete; // TRUE => delete document when no more views BOOL m_bEmbedded; // TRUE => document is being created by OLE #ifdef _DEBUG virtual void Dump(CDumpContext&) const; virtual void AssertValid() const; #endif //_DEBUG virtual ~CDocument(); // implementation helpers virtual BOOL DoSave(LPCTSTR lpszPathName, BOOL bReplace = TRUE); virtual BOOL DoFileSave(); virtual void UpdateFrameCounts(); void DisconnectViews(); void SendInitialUpdate(); // overridables for implementation virtual HMENU GetDefaultMenu(); // get menu depending on state virtual HACCEL GetDefaultAccelerator(); virtual void OnIdle(); virtual void OnFinalRelease(); virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo); friend class CDocTemplate; protected: // file menu commands //{{AFX_MSG(CDocument) afx_msg void OnFileClose(); afx_msg void OnFileSave(); afx_msg void OnFileSaveAs(); //}}AFX_MSG // mail enabling afx_msg void OnFileSendMail(); afx_msg void OnUpdateFileSendMail(CCmdUI* pCmdUI); DECLARE_MESSAGE_MAP() }; |
void CDocument::AddView(CView* pView) { ASSERT_VALID(pView); ASSERT(pView->m_pDocument == NULL); // must not be already attached ASSERT(m_viewList.Find(pView, NULL) == NULL); // must not be in list m_viewList.AddTail(pView); ASSERT(pView->m_pDocument == NULL); // must be un-attached pView->m_pDocument = this; OnChangedViewList(); // must be the last thing done to the document } |
void CDocument::RemoveView(CView* pView) { ASSERT_VALID(pView); ASSERT(pView->m_pDocument == this); // must be attached to us m_viewList.RemoveAt(m_viewList.Find(pView)); pView->m_pDocument = NULL; OnChangedViewList(); // must be the last thing done to the document } |
void CDocument::OnChangedViewList() { // if no more views on the document, delete ourself // not called if directly closing the document or terminating the app if (m_viewList.IsEmpty() && m_bAutoDelete) { OnCloseDocument(); return; } // update the frame counts as needed UpdateFrameCounts(); } |
void CDocument::DisconnectViews() { while (!m_viewList.IsEmpty()) { CView* pView = (CView*)m_viewList.RemoveHead(); ASSERT_VALID(pView); ASSERT_KINDOF(CView, pView); pView->m_pDocument = NULL; } } |
POSITION CDocument::GetFirstViewPosition() const { return m_viewList.GetHeadPosition(); } CView* CDocument::GetNextView(POSITION& rPosition) const { ASSERT(rPosition != BEFORE_START_POSITION); // use CDocument::GetFirstViewPosition instead ! if (rPosition == NULL) return NULL; // nothing left CView* pView = (CView*)m_viewList.GetNext(rPosition); ASSERT_KINDOF(CView, pView); return pView; } |
CFile* CDocument::GetFile(LPCTSTR lpszFileName, UINT nOpenFlags, CFileException* pError) { CMirrorFile* pFile = new CMirrorFile; ASSERT(pFile != NULL); if (!pFile->Open(lpszFileName, nOpenFlags, pError)) { delete pFile; pFile = NULL; } return pFile; } void CDocument::ReleaseFile(CFile* pFile, BOOL bAbort) { ASSERT_KINDOF(CFile, pFile); if (bAbort) pFile->Abort(); // will not throw an exception else pFile->Close(); delete pFile; } |
BOOL CDocument::OnOpenDocument(LPCTSTR lpszPathName) { if (IsModified()) TRACE0("Warning: OnOpenDocument replaces an unsaved document.\n"); CFileException fe; CFile* pFile = GetFile(lpszPathName, CFile::modeRead|CFile::shareDenyWrite, &fe); if (pFile == NULL) { ReportSaveLoadException(lpszPathName, &fe,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); return FALSE; } DeleteContents(); SetModifiedFlag(); // dirty during de-serialize CArchive loadArchive(pFile, CArchive::load | CArchive::bNoFlushOnDelete); loadArchive.m_pDocument = this; loadArchive.m_bForceFlat = FALSE; TRY { CWaitCursor wait; if (pFile->GetLength() != 0) Serialize(loadArchive); // load me loadArchive.Close(); ReleaseFile(pFile, FALSE); } CATCH_ALL(e) { ReleaseFile(pFile, TRUE); DeleteContents(); // remove failed contents TRY { ReportSaveLoadException(lpszPathName, e,FALSE, AFX_IDP_FAILED_TO_OPEN_DOC); } END_TRY DELETE_EXCEPTION(e); return FALSE; } END_CATCH_ALL SetModifiedFlag(FALSE); // start off with unmodified return TRUE; } |
BOOL CDocument::OnSaveDocument(LPCTSTR lpszPathName) { CFileException fe; CFile* pFile = NULL; pFile = GetFile(lpszPathName, CFile::modeCreate |CFile::modeReadWrite | CFile::shareExclusive, &fe); if (pFile == NULL) { ReportSaveLoadException(lpszPathName, &fe,TRUE, AFX_IDP_INVALID_FILENAME); return FALSE; } CArchive saveArchive(pFile, CArchive::store | CArchive::bNoFlushOnDelete); saveArchive.m_pDocument = this; saveArchive.m_bForceFlat = FALSE; TRY { CWaitCursor wait; Serialize(saveArchive); // save me saveArchive.Close(); ReleaseFile(pFile, FALSE); } CATCH_ALL(e) { ReleaseFile(pFile, TRUE); TRY { ReportSaveLoadException(lpszPathName, e,TRUE, AFX_IDP_FAILED_TO_SAVE_DOC); } END_TRY DELETE_EXCEPTION(e); return FALSE; } END_CATCH_ALL SetModifiedFlag(FALSE); // back to unmodified return TRUE; // success } |
void CDocument::OnCloseDocument() // must close all views now (no prompting) - usually destroys this { // destroy all frames viewing this document // the last destroy may destroy us BOOL bAutoDelete = m_bAutoDelete; m_bAutoDelete = FALSE; // don't destroy document while closing views while (!m_viewList.IsEmpty()) { // get frame attached to the view CView* pView = (CView*)m_viewList.GetHead(); ASSERT_VALID(pView); CFrameWnd* pFrame = pView->GetParentFrame(); ASSERT_VALID(pFrame); // and close it PreCloseFrame(pFrame); pFrame->DestroyWindow(); // will destroy the view as well } m_bAutoDelete = bAutoDelete; // clean up contents of document before destroying the document itself DeleteContents(); // delete the document if necessary if (m_bAutoDelete) delete this; } |
void CExampleDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here ar << var1 << var2; } else { // TODO: add loading code here var2 >> var1 >> ar; } } |
void CDocument::UpdateAllViews(CView* pSender, LPARAM lHint, CObject* pHint) // walk through all views { ASSERT(pSender == NULL || !m_viewList.IsEmpty()); // must have views if sent by one of them POSITION pos = GetFirstViewPosition(); while (pos != NULL) { CView* pView = GetNextView(pos); ASSERT_VALID(pView); if (pView != pSender) pView->OnUpdate(pSender, lHint, pHint); } } |
afx_msg void OnFileNew(); afx_msg void OnFileOpen(); |
virtual void OnFileNew(); virtual void OnFileOpen(); virtual CDocument* OpenDocumentFile(LPCTSTR lpszFileName); // open named file |
virtual CDocument* OpenDocumentFile( LPCTSTR lpszPathName, BOOL bMakeVisible = TRUE) = 0; // open named file // if lpszPathName == NULL => create new file with this type virtual CDocument* CreateNewDocument(); |
virtual BOOL OnNewDocument(); virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); |
void CDocManager::OnFileNew() { if (m_templateList.IsEmpty()) { ... return ; } //取第一个文档模板的指针 CDocTemplate *pTemplate = (CDocTemplate*)m_templateList.GetHead(); if (m_templateList.GetCount() > 1) { // 如果多于一个文档模板,弹出对话框提示用户选择 CNewTypeDlg dlg(&m_templateList); int nID = dlg.DoModal(); if (nID == IDOK) pTemplate = dlg.m_pSelectedTemplate; else return ; // none - cancel operation } … //参数为NULL的时候OpenDocument File会新建一个文件 pTemplate->OpenDocumentFile(NULL); } |
图3.3视图的创建顺序 |
class CView : public CWnd { DECLARE_DYNAMIC(CView) // Constructors protected: CView(); // Attributes public: CDocument* GetDocument() const; // Operations public: // for standard printing setup (override OnPreparePrinting) BOOL DoPreparePrinting(CPrintInfo* pInfo); // Overridables public: virtual BOOL IsSelected(const CObject* pDocItem) const; // support for OLE // OLE scrolling support (used for drag/drop as well) virtual BOOL OnScroll(UINT nScrollCode, UINT nPos, BOOL bDoScroll = TRUE); virtual BOOL OnScrollBy(CSize sizeScroll, BOOL bDoScroll = TRUE); // OLE drag/drop support virtual DROPEFFECT OnDragEnter(COleDataObject* pDataObject,DWORD dwKeyState, CPoint point); virtual DROPEFFECT OnDragOver(COleDataObject* pDataObject,DWORD dwKeyState, CPoint point); virtual void OnDragLeave(); virtual BOOL OnDrop(COleDataObject* pDataObject,DROPEFFECT dropEffect, CPoint point); virtual DROPEFFECT OnDropEx(COleDataObject* pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point); virtual DROPEFFECT OnDragScroll(DWORD dwKeyState, CPoint point); virtual void OnPrepareDC(CDC* pDC, CPrintInfo* pInfo = NULL); virtual void OnInitialUpdate(); // called first time after construct protected: // Activation virtual void OnActivateView(BOOL bActivate, CView* pActivateView,CView* pDeactiveView); virtual void OnActivateFrame(UINT nState, CFrameWnd* pFrameWnd); // General drawing/updating virtual void OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint); virtual void OnDraw(CDC* pDC) = 0; // Printing support virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); // must override to enable printing and print preview virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); virtual void OnPrint(CDC* pDC, CPrintInfo* pInfo); virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); // Advanced: end print preview mode, move to point virtual void OnEndPrintPreview(CDC* pDC, CPrintInfo* pInfo, POINT point,CPreviewView* pView); // Implementation public: virtual ~CView(); #ifdef _DEBUG virtual void Dump(CDumpContext&) const; virtual void AssertValid() const; #endif //_DEBUG // Advanced: for implementing custom print preview BOOL DoPrintPreview(UINT nIDResource, CView* pPrintView,CRuntimeClass* pPreviewViewClass, CPrintPreviewState* pState); virtual void CalcWindowRect(LPRECT lpClientRect,UINT nAdjustType = adjustBorder); virtual CScrollBar* GetScrollBarCtrl(int nBar) const; static CSplitterWnd* PASCAL GetParentSplitter(const CWnd* pWnd, BOOL bAnyState); protected: CDocument* m_pDocument; public: virtual BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo); protected: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); virtual void PostNcDestroy(); // friend classes that call protected CView overridables friend class CDocument; friend class CDocTemplate; friend class CPreviewView; friend class CFrameWnd; friend class CMDIFrameWnd; friend class CMDIChildWnd; friend class CSplitterWnd; friend class COleServerDoc; friend class CDocObjectServer; //{{AFX_MSG(CView) afx_msg int OnCreate(LPCREATESTRUCT lpcs); afx_msg void OnDestroy(); afx_msg void OnPaint(); afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message); // commands afx_msg void OnUpdateSplitCmd(CCmdUI* pCmdUI); afx_msg BOOL OnSplitCmd(UINT nID); afx_msg void OnUpdateNextPaneMenu(CCmdUI* pCmdUI); afx_msg BOOL OnNextPaneCmd(UINT nID); // not mapped commands - must be mapped in derived class afx_msg void OnFilePrint(); afx_msg void OnFilePrintPreview(); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; |
CView::~CView() { if (m_pDocument != NULL) m_pDocument->RemoveView(this); } |
///////////////////////////////////////////////////////////////////////////// // CExampleView drawing void CExampleView::OnDraw(CDC* pDC) { CExampleDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here } CView::PreCreateWindow负责View的初始化: ///////////////////////////////////////////////////////////////////////////// // CView second phase construction - bind to document BOOL CView::PreCreateWindow(CREATESTRUCT & cs) { ASSERT(cs.style & WS_CHILD); if (cs.lpszClass == NULL) { VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background } if (afxData.bWin4 && (cs.style & WS_BORDER)) { cs.dwExStyle |= WS_EX_CLIENTEDGE; cs.style &= ~WS_BORDER; } return TRUE; } |
void CView::OnUpdate(CView* pSender, LPARAM /*lHint*/, CObject* /*pHint*/) { ASSERT(pSender != this); UNUSED(pSender); // unused in release builds // invalidate the entire pane, erase background too Invalidate(TRUE); } |
图5.1 在AppWizard中选择框架窗口 |
BOOL 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 destroyed HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU); if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL) { TRACE0("Warning: failed to load menu for CFrameWnd.\n"); PostNcDestroy(); // perhaps delete the C++ object return FALSE; } } m_strTitle = lpszWindowName; // save title for later if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd ->GetSafeHwnd(), hMenu, (LPVOID)pContext)) { TRACE0("Warning: failed to create CFrameWnd.\n"); if (hMenu != NULL) DestroyMenu(hMenu); return FALSE; } return TRUE; } |
BOOL CFrameWnd::LoadFrame(UINT nIDResource, DWORD dwDefaultStyle, CWnd *pParentWnd, CCreateContext *pContext) { // only do this once ASSERT_VALID_IDR(nIDResource); ASSERT(m_nIDHelp == 0 || m_nIDHelp == nIDResource); m_nIDHelp = nIDResource; // ID for help context (+HID_BASE_RESOURCE) CString strFullString; if (strFullString.LoadString(nIDResource)) AfxExtractSubString(m_strTitle, strFullString, 0); // first sub-string VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); // attempt to create the window LPCTSTR lpszClass = GetIconWndClass(dwDefaultStyle, nIDResource); LPCTSTR lpszTitle = m_strTitle; if (!Create(lpszClass, lpszTitle, dwDefaultStyle, rectDefault, pParentWnd, MAKEINTRESOURCE(nIDResource), 0L, pContext)) { return FALSE; // will self destruct on failure normally } // save the default menu handle ASSERT(m_hWnd != NULL); m_hMenuDefault = ::GetMenu(m_hWnd); // load accelerator resource LoadAccelTable(MAKEINTRESOURCE(nIDResource)); if (pContext == NULL) // send initial update SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE); return TRUE; } |
BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT &cs) { if (cs.lpszClass == NULL) { VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG)); cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background } if ((cs.style &FWS_ADDTOTITLE) && afxData.bWin4)cs.style |= FWS_PREFIXTITLE; if (afxData.bWin4) cs.dwExStyle |= WS_EX_CLIENTEDGE; return TRUE; } |
void CMdiView::OnReplaceMenu() { // Load a new menu resource named IDR_SHORT_MENU CMdiDoc* pdoc = GetDocument(); pdoc->m_DefaultMenu = ::LoadMenu(AfxGetResourceHandle(), MAKEINTRESOURCE(IDR_SHORT_MENU)); if (pdoc->m_DefaultMenu == NULL) return; // Get the parent window of this view window. The parent window is // a CMDIChildWnd-derived class. We can then obtain the MDI parent // frame window using the CMDIChildWnd*. Then, replace the current // menu bar with the new loaded menu resource. CMDIFrameWnd* frame = ((CMDIChildWnd *) GetParent())->GetMDIFrame(); frame->MDISetMenu(CMenu::FromHandle(pdoc->m_DefaultMenu), NULL); frame->DrawMenuBar(); } |
BOOL CMDIFrameWnd::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext*) { CMenu* pMenu = NULL; if (m_hMenuDefault == NULL) { // default implementation for MFC V1 backward compatibility pMenu = GetMenu(); ASSERT(pMenu != NULL); // This is attempting to guess which sub-menu is the Window menu. // The Windows user interface guidelines say that the right-most // menu on the menu bar should be Help and Window should be one // to the left of that. int iMenu = pMenu->GetMenuItemCount() - 2; // If this assertion fails, your menu bar does not follow the guidelines // so you will have to override this function and call CreateClient // appropriately or use the MFC V2 MDI functionality. ASSERT(iMenu >= 0); pMenu = pMenu->GetSubMenu(iMenu); ASSERT(pMenu != NULL); } return CreateClient(lpcs, pMenu); } |
BOOL CMDIFrameWnd::CreateClient(LPCREATESTRUCT lpCreateStruct, CMenu* pWindowMenu) { ASSERT(m_hWnd != NULL); ASSERT(m_hWndMDIClient == NULL); DWORD dwStyle = WS_VISIBLE | WS_CHILD | WS_BORDER | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | MDIS_ALLCHILDSTYLES; // allow children to be created invisible DWORD dwExStyle = 0; // will be inset by the frame if (afxData.bWin4) { // special styles for 3d effect on Win4 dwStyle &= ~WS_BORDER; dwExStyle = WS_EX_CLIENTEDGE; } CLIENTCREATESTRUCT ccs; ccs.hWindowMenu = pWindowMenu->GetSafeHmenu(); // set hWindowMenu for MFC V1 backward compatibility // for MFC V2, window menu will be set in OnMDIActivate ccs.idFirstChild = AFX_IDM_FIRST_MDICHILD; if (lpCreateStruct->style & (WS_HSCROLL|WS_VSCROLL)) { // parent MDIFrame's scroll styles move to the MDICLIENT dwStyle |= (lpCreateStruct->style & (WS_HSCROLL|WS_VSCROLL)); // fast way to turn off the scrollbar bits (without a resize) ModifyStyle(WS_HSCROLL|WS_VSCROLL, 0, SWP_NOREDRAW|SWP_FRAMECHANGED); } // Create MDICLIENT control with special IDC if ((m_hWndMDIClient = ::CreateWindowEx(dwExStyle, _T("mdiclient"), NULL, dwStyle, 0, 0, 0, 0, m_hWnd, (HMENU)AFX_IDW_PANE_FIRST, AfxGetInstanceHandle(), (LPVOID)&ccs)) == NULL) { TRACE(_T("Warning: CMDIFrameWnd::OnCreateClient: failed to create MDICLIENT.") _T(" GetLastError returns 0x%8.8X\n"), ::GetLastError()); return FALSE; } // Move it to the top of z-order ::BringWindowToTop(m_hWndMDIClient); return TRUE; } |
CMDIFrameWnd *CMDIChildWnd::GetMDIFrame() { HWND hWndMDIClient = ::GetParent(m_hWnd); CMDIFrameWnd *pMDIFrame; pMDIFrame = (CMDIFrameWnd*)CWnd::FromHandle(::GetParent(hWndMDIClient)); return pMDIFrame; } |
BOOL CChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext *pContext) { … if (!m_wndSplitter.Create(this, 2, 2, // 分割的行、列数 CSize(10, 10), // 最小化尺寸 pContext)) { TRACE0("创建分割失败"); return FALSE; } … return TRUE; } |
从该对象 | 如何访问其他对象 |
全局函数 | 调用全局函数AfxGetApp可以得到CWinApp应用类指针 |
应用 | AfxGetApp()->m_pMainWnd为框架窗口指针;用CWinApp::GetFirstDocTemplatePostion、CWinApp::GetNextDocTemplate来遍历所有文档模板 |
文档 | 调用CDocument::GetFirstViewPosition,CDocument::GetNextView来遍历所有和文档关联的视图;调用CDocument:: GetDocTemplate 获取文档模板指针 |
文档模板 | 调用CDocTemplate::GetFirstDocPosition、CDocTemplate::GetNextDoc来遍历所有对应文档 |
视图 | 调用CView::GetDocument 得到对应的文档指针; 调用CView::GetParentFrame 获取框架窗口 |
文档框架窗口 | 调用CFrameWnd::GetActiveView 获取当前得到当前活动视图指针; 调用CFrameWnd::GetActiveDocument 获取附加到当前视图的文档指针 |
MDI 框架窗口 | 调用CMDIFrameWnd::MDIGetActive 获取当前活动的MDI子窗口(CMDIChildWnd) |
CMyApp *pMyApp = (CMyApp*)AfxGetApp(); //得到应用程序指针 POSITION p = pMyApp->GetFirstDocTemplatePosition();//得到第1个文档模板 while (p != NULL) //遍历文档模板 { CDocTemplate *pDocTemplate = pMyApp->GetNextDocTemplate(p); POSITION p1 = pDocTemplate->GetFirstDocPosition();//得到文档模板对应的第1个文档 while (p1 != NULL) //遍历文档模板对应的文档 { CDocument *pDocument = pDocTemplate->GetNextDoc(p1); POSITION p2 = pDocument->GetFirstViewPosition(); //得到文档对应的第1个视图 while (p2 != NULL) //遍历文档对应的视图 { CView *pView = pDocument->GetNextView(p2); } } } |
图6.2 文档/视图框架程序类的层次关系 |
图6.3文档/视图的三种关系 |
//视图中的消息映射和处理函数 BEGIN_MESSAGE_MAP(CExampleView, CView) //{{AFX_MSG_MAP(CExampleView) ON_COMMAND(IDM_SELF, OnSelf) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CExampleView::OnSelf() { // TODO: Add your command handler code here AfxMessageBox("消息在视图中处理"); } //框架窗口中的消息映射和处理函数 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) ON_COMMAND(IDM_SELF, OnSelf) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CMainFrame::OnSelf() { // TODO: Add your command handler code here AfxMessageBox("消息在框架窗口中处理"); } |
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { // pump through current view FIRST CView* pView = GetActiveView(); if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; // then pump through frame if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; // last but not least, pump through app CWinApp* pApp = AfxGetApp(); if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; return FALSE; } BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) { // first pump through pane if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; // then pump through document BOOL bHandled = FALSE; if (m_pDocument != NULL) { // special state for saving view before routing to document _AFX_THREAD_STATE* pThreadState = AfxGetThreadState(); CView* pOldRoutingView = pThreadState->m_pRoutingView; pThreadState->m_pRoutingView = this; bHandled = m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); pThreadState->m_pRoutingView = pOldRoutingView; } return bHandled; } |
//文档的消息映射和处理函数 BEGIN_MESSAGE_MAP(CExampleDoc, CDocument) //{{AFX_MSG_MAP(CExampleDoc) ON_COMMAND(IDM_SELF, OnSelf) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CExampleDoc::OnSelf() { // TODO: Add your command handler code here AfxMessageBox("消息在文档中处理"); } //应用的消息映射和处理函数 BEGIN_MESSAGE_MAP(CExampleApp, CWinApp) //{{AFX_MSG_MAP(CExampleApp) ON_COMMAND(IDM_SELF, OnSelf) //}}AFX_MSG_MAP END_MESSAGE_MAP() void CExampleApp::OnSelf() { // TODO: Add your command handler code here AfxMessageBox("消息在应用中处理"); } |
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by EXAMPLE.RC // #define IDD_ABOUTBOX 100 #define IDR_MAINFRAME 128 //#define IDR_EXAMPLTYPE 129 #define IDR_TEXTTYPE 10001 #define IDR_BMPTYPE 10002 … #endif STRINGTABLE PRELOAD DISCARDABLE BEGIN IDR_MAINFRAME "Example" IDR_EXAMPLTYPE "\nExampl\nExampl\n\n\nExample.Document\nExampl Document" IDR_TEXTTYPE "\nTEXT\nTEXT\nExampl 文件 (*.txt)\n.txt\nTEXT\nTEXT Document" IDR_BMPTYPE "\nBMP\nBMP\nExampl 文件 (*.bmp)\n.bmp\nBMP\nBMP Document" END |
BOOL CExampleApp::InitInstance() { … CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_TEXTTYPE, //对应文本文件的字符串 RUNTIME_CLASS(CExampleDoc), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CExampleView)); AddDocTemplate(pDocTemplate); … } |
class CExampleDoc : public CDocument { … CString m_Text; //在文档类中定义成员变量用于存储TXT文件中的字符串 … } //重载文档类的Serialize,读取字符串到m_Text中 void CExampleDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here ar.ReadString(m_Text); } } //重载视图类的OnDraw函数,显示文档中的字符串 ///////////////////////////////////////////////////////////////////////////// // CExampleView drawing void CExampleView::OnDraw(CDC* pDC) { CExampleDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here pDC->TextOut(0,0,pDoc->m_Text); } |
图7.2 打开TXT文件的界面 |
#include "BMPDocument.h" #include "BMPView.h" |
pDocTemplate = new CMultiDocTemplate( //IDR_EXAMPLTYPE, IDR_BMPTYPE, RUNTIME_CLASS(CBMPDocument), RUNTIME_CLASS(CChildFrame), // custom MDI child frame RUNTIME_CLASS(CBMPView)); AddDocTemplate(pDocTemplate); |
图7.4 包含多个文档模板后的"打开" |
class CBMPView : public CView { … CBMPDocument* GetDocument(); //头文件中声明 … } //重载CBMPView::GetDocument函数 CBMPDocument* CBMPView::GetDocument() { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CBMPDocument))); return (CBMPDocument*)m_pDocument; } |
void CBMPView::OnDraw(CDC* pDC) { CBMPDocument* pDoc = GetDocument(); // TODO: add draw code here CDib dib; dib.Load(pDoc->GetPathName()); dib.SetPalette(pDC); dib.Draw(pDC); } |
图7.5 打开位图的界面 |
图7.6 同时打开位图和文本的界面 |
class CBMPDocument : public CDocument { … public: unsigned char bmpBit[MAX_BITMAP]; } void CBMPDocument::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO: add storing code here } else { // TODO: add loading code here CFile *file = ar.GetFile(); for(int i=0;i<file->GetLength();i++) { ar >> bmpBit[i]; } } } |
class CBMPChildFrame : public CMDIChildWnd { … public: CSplitterWnd m_wndSplitter; //定义拆分 … } |
BOOL CBMPChildFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext *pContext) { // TODO: Add your specialized code here and/or call the base class CRect rect; GetClientRect(&rect); m_wndSplitter.CreateStatic(this, 1, 2); m_wndSplitter.CreateView(0, 0, pContext->m_pNewViewClass, CSize(rect.right /2, 0), pContext); m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CBMPDataView), CSize(0, 0),pContext); m_wndSplitter.SetActivePane(0, 0); return true; } |
void CBMPDataView::OnDraw(CDC* pDC) { CBMPDocument* pDoc = GetDocument(); // TODO: add draw code here CString str; char tmp[3]; for(int i=0;i<20;i++)//假设只显示20行,每行20个字符 { str = ""; for (int j =0;j<20;j++) { memset(tmp,0,4); itoa(pDoc->bmpBit[10*i+j],tmp,16); str+=CString(tmp)+" "; } pDC->TextOut(0,20*i,str); } } |
图7.7 用两种视图来显示位图的界面 图7.8 "多视图+多文档"的界面 |