• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
                  Visual C++ 6.0 以其功能強大、用戶界面友好而倍受程序員們的青睞。但是,在當前的Microsoft 基本類庫4.2 版本中,大約有將近200 個類,數千個函數,加之Microsoft 公司隱藏了一些技術細節,使得人們深入學習MFC變得十分困難。 

              MFC的AppWizard可以生成三種類型的應用程序:基于對話框的應用、單文檔應用(SDI)和多文檔應用(MDI)。前兩者的結構較簡單,本文不再贅敘。筆者擬從MFC中的文檔/視結構入手,分析一些函數的流程,并解決編制MDI 應用程序過程中的一些常見問題。 

            (一)、了解文檔/視結構 

              MFC應用程序模型歷經多年以有了相當大的發展。有一個時期,它只是個使用應用程序對象和主窗口對象的簡單模型。在這個模型中,應用程序的數據作為成員變量保持在框架窗口類中,在框架窗口的客戶區中,該數據被提交顯示器。隨著MFC2。0的問世,一種應用程序結構的新方式----MFC文檔/視結構出現了。在這種結構中,CFrameWnd繁重的任務被委派給幾個不同類,實現了數據存儲和顯示的分離。一般情況下,采用文檔/視結構的應用程序至少應由以下對象組成: 

            。應用程序是一個CwinApp派生對象,它充當全部應用程序的容器。應用程序沿消息映射網絡分配消息給它的所有子程序。 
            。框架窗口是一CfrmeWnd派生對象。 
            。文檔是一個CDocument派生對象,它存儲應用程序的數據,并把這些信息提供給應用程序的其余部分。 
            。視窗是Cview派生對象,它與其父框架窗口用戶區對齊。視窗接受用戶對應用程序的輸入并顯示相關聯的文檔數據。 

                  通常,應用程序數據存在于簡單模型中的框架窗口中。在文檔/視方式中,該數據移入稱為document的獨立數據對象。當然,文檔不一定是文字,文檔是可以表現應用程序使用的數據集的抽象術語。而用戶輸入處理及圖形輸出功能從框架窗口轉向視圖。單獨的視窗完全遮蔽框架窗口的客戶區,這意味著即使程序員直接繪畫至框架窗口的客戶區,視圖仍遮蔽繪畫,在屏幕上不出現任何信息。所以輸出必須通過視圖。框架窗口僅僅是個視圖容器。 

                  CDocument類對文檔的建立及歸檔提供支持并提供應用程序用于控制其數據的接口。MDI應用程序可以處理多個類型的文檔,每個類型的文檔擁有一個相關聯的文檔模板對象。文檔對象駐留在場景后面,提供由視圖對象顯示的信息。文檔至少有一個相關聯的視圖。視圖只能與一個文檔相關聯。
             
                  在文檔/視方式中,對象的建立是由文檔模板來管理的,它是CDocTemplate派生對象,建立并維護框架窗口,文檔及視。 

                  MFC調用命令處理程序以響應發生在應用程序中的事件。命令發送的優先級是: 

                  活動的視圖->框架窗口->文檔->應用程序->默認窗口過程(DefWindowsProc) 

                  總之,在文檔/視方式中,文檔和視是分離的,即:文檔用于保存數據,而視是用來顯示這些數據。文檔模板維護它們之間的關西。這種文檔/視結構在開發大型軟件項目時特別有用。 

            (二)、了解與文檔/視結構有關的各種類之間的關系。 

                  在文檔/視應用程序中,CWinApp對象擁有并控制文檔模板,后者產生文檔、框架窗口及視窗。

                  從用戶的角度來看,“視”實際上是一個普通的窗口。象其他基于Widnows應用的窗口一樣,人們可以改變它的尺寸,對它進行移動,也可以隨時關閉它。若從程序員的角度來看,視實際上是一個從MFC類庫中的Cview類所派生出的類的對象。文檔對象是用來保存數據的,而視對象是用來顯示數據的,并且允許對數據進行編輯。SDI或MDI的文檔類是由Cdocument類派生出來的,它可以有一個或多個視類,而這些視類最終都是由Cview類派生出來的。視對象只有一個與之相聯系的文檔對象,它所包含的CView::GetDocument函數允許應用在視中得到與之相聯系的文檔,據此,應用程序可以對文檔類成員函數及公共數據成員進行訪問。如果視對象接受到了一條消息,表示用戶在編輯控制中輸入了新的數據,此時,視就必須通知文檔對象對其內部數據進行相應的更新。 

                  如果文檔數據發生了變化,則所有的視都必須被通知到,以便它們能夠對所顯示的數據進行相應的更新。Cdocument::UpdateAllViews函數即可完成此功能。當該函數被調用時,派生視類的CView::OnUpdate函數被觸發。通常OnUpdate函數要對文檔進行訪問,讀取文檔數據,然后再對視的數據成員或控制進行更新,以便反映出文檔的變化。另外,還可以利用OnUpdate函數使視的部分客戶區無效,以便觸發Cview::OnDraw函數,利用文檔數據來重新對窗口進行繪制。 

                  在MDI應用程序中,可以處理多個文檔類型,即多個文檔模板,每個模板又可以有多個文檔,每個文檔又可以多視顯示。為管理方便,上一級往往保留了下一級的指針列表。解釋如下: 

            (1)、每個應用程序類(CwinApp的派生類)都保留并維護了一份所有文檔模板的指針列表,這是一個鏈表結構。應用程序為所要支持的每個文檔類型動態分配一個CMultiDocTemplate 對象, 
            CmultiDocTemplate(UINT nIDResource, 
            CruntimeClass * pDocClass, 
            CruntimeClass * pFrameClass, 
            CruntimeClass * pViewClass ); 

                  并在應用程序類的CWinApp::InitInstance成員函數中將每個CMultiDocTemplate對象傳遞給CWinApp::AddDocTemplate。 該函數將一個文檔模板加入到應用程序可用文檔模板的列表中。函數原形為: 

            void AddDocTemplate(CdocTemplate * pTemplate); 

                  應用程序可以用CWinApp::GetFirstDocTemplatePostion獲得應用程序注冊的第一個文檔模板的位置,利用該值來調用CWinApp::GetNextDocTemplate函數,獲得第一個CDocTemplate對象指針。函數原形如下:
             
            POSITION GetFirstDocTemplate( ) const; 
            CDocTemplate *GetNextDocTemplate( POSITION & pos ) const; 

                  第二個函數返回由pos 標識的文檔模板。POSITION是MFC定義的一個用于迭代或對象指針檢索的值。通過這兩個函數,應用程序可以遍歷整個文檔模板列表。如果被檢索的文檔模板是模板列表中的最后一個,則pos參數被置為NULL。 

            (2)、一個文檔模板可以有多個文檔,每個文檔模板都保留并維護了一個所有對應文檔的指針列表。應用程序可以用CDocTemplate::GetFirstDocPosition函數獲得與文檔模板相關的文檔集合中第一個文檔的位置,并用POSITION值作為CDocTemplate::GetNextDoc的參數來重復遍歷與模板相關的文檔列表。函數原形為:
             
            viaual POSITION GetFirstDocPosition( ) const = 0; 
            visual Cdocument *GetNextDoc(POSITION & rPos) const = 0; 

            如果列表為空,則rPos被置為NULL. 

            (3)、在文檔中可以調用CDocument::GetDocTemplate獲得指向該文檔模板的指針。函數原形如下: 

            CDocTemplate * GetDocTemplate ( ) const; 

            如果該文檔不屬于文檔模板管理,則返回值為NULL。 

            (4)、一個文檔可以有多個視。每一個文檔都保留并維護一個所有相關視的列表。CDocument::AddView將一個視連接到文檔上,將該視加入到文檔相聯系的視的列表中,并將視的文檔指針指向該文檔。當有File/New、File/Open、Windows/New或Window/Split的命令而將一個新創建的視的對象連接到文檔上時, MFC會自動調用該函數,框架通過文檔/視的結構將文檔和視聯系起來。當然,程序員也可以根據自己的需要調用該函數。 

            Virtual POSITION GetFirstViewPosition( ) const; 
            Virtual CViw * GetNextView( POSITION &rPosition) cosnt; 

                  應用程序可以調用CDocument::GetFirstViewPosition返回與調用文檔相聯系的視的列表中的第一個視的位置,并調用CDocument::GetNextView返回指定位置的視,并將rPositon的值置為列表中下一個視的POSITION值。如果找到的視為列表中的最后一個視,則將rPosition置為NULL. 

                  當在文檔上新增一個視或刪除一個視時,MFC會調用OnChangeViewList函數。如果被刪除的視是該文檔的最后一個視,則刪除該文檔。 

            (5)、一個視只能有一個文檔。在視中,調用CView::GetDocument可以獲得一個指向視的文檔的指針。函數原形如下: 

            CDocument *GetDocument ( ) const; 

            如果該視不與任何文檔相,則返回NULL. 

            (6)、MDI框架窗口通過調用CFrameWnd::GetActiveDocument 可以獲得與當前活動的視相連的CDocument 指針。函數原形如下: 

            virtual CDocument * GetActiveDocument( ); 

            (7)、通過調用CFrameWnd::GetActiveView 可以獲得指向與CFrameWnd框架窗口連接的活動視的指針,如果是被CMDIFrameWnd框架窗口調用,則返回NULL。MDI框架窗口可以首先調用MDIGetActive找到活動的MDI子窗口,然后找到該子窗口的活動視。函數原形如下: 

            virtual Cdocument * GetActiveDocument( ); 

            (8)、MDI框架窗口通過調用CFrameWnd::GetActiveFrame, 可以獲得一個指向MDI框架窗口的活動多文檔界面子窗口的指針。 

            (9)、CMDIChildWnd調用GetMDIFrame獲得MDI框架窗口(CMDIFrameWnd)。 

            (10)、CWinApp 調用AfxGetMainWnd得到指向應用程序的活動主窗口的指針。 

                  下面一段代碼,就是利用CDocTemplate、CDocument和CView之間的存取關系,遍歷整個文檔模板、文檔以及視。 

                  CMyApp * pMyApp = (CMyApp *)AfxGetApp(); 
                  POSITION p = pMyApp->GetFirstDocTemplatePosition(); 
                  while(p!= NULL) 
                  { 
                        CDocTemplate * pDocTemplate = pMyApp->GetNextDocTemplate(p); 
                        POSITION p1 = pDocTemplate->GetFirstDocPosition(); 
                        while(p1 != NULL) 
                        { 
                              CDocument * pDocument = pDocTemplate->GetNextDoc(p1); 
                              POSITION p2 = pDocument->GetFirstViewPosition(); 
                              while(p2 != NULL) 
                              { 
                                    CView * pView = pDocument->GetNextView(p2); 
                              } 
                        } 


            (圖4)、遍歷整個文檔模板、文檔和視 

                  在應用程序的任何地方,程序員都可以調用AfxGetApp( )獲得應用程序的對象指針。由于本文著重介紹文檔/視的關系,至于框架窗口之間的關系沒能列全,讀者可以查相應的文檔。 

            (三)、了解CwinApp::OnFileNew、CwinApp::OnFileOpen和Window/New的程序流程。 

            (1)、CwinApp::OnFileNew和CwinApp::OnFileOpen函數的簡單流程。 
            -------------------------------------------------------------------------------- 
            -------------------------------------------------------------------------------- 
            -------------------------------------------------------------------------------- 
            -------------------------------------------------------------------------------- 
            -------------------------------------------------------------------------------- 
            -------------------------------------------------------------------------------- 
            -------------------------------------------------------------------------------- 

                  在CWinApp::OnFile/new 或CwinApp::OnFileOpen函數中,核心操作是CDocTemplate::OpenDocument函數。其函數原型為: 

            virtual CDocument* CDocTemplate::OpenDocumentFile(LPCTSTR lpszPathName, 
                  BOOL bMakeVisible =TRUE ) = 0; 

            圖(4)中星號標注之后即是該函數的流程,簡要介紹如下: 

            (1)、CDocTemplate::CreateNewDocument函數創建一個新文檔,其類型與文檔模板相關,并通過函數CDocTemplate::AddDocument加入該文檔模板的文檔指針列表中。此時,文檔類的構造函數被執行,程序可以在此進行文檔的初始化。 

            (2)、函數CDocTemplate::CreateNewFrame調用MDI子窗口類(CMDIChildWnd)的構造函數,生成MDI子窗口對象。接著調用CMDIChildWnd::PreCreateWindow。然后,生成一個CCreateContext對象,(CcreateContext是MFC框架所使用的一種結構,它將構成文檔和視的組件聯系起來。后文將詳細介紹之。)并將該對象值傳給CMDIChildWnd::OnCreateClient函數。MFC調用此函數,用CCreateContext對象提供的信息創建一個或多個CView對象。此時,各視的構造函數被依次調用。 

            (3)、接著,判斷lpszPathName是否為空。分為兩種情況: 

            (a)、若為空,則表明要創建一個新文檔:調用SetDefaultTitle函數裝載文檔的缺省標題,并顯示在文檔的標題欄中;然后執行CDocument::OnNewDocument。該函數調用DeleteContents以保證文檔為空,然后置新文檔為清潔。可以重載該函數。 

            (b)、否則,表明要打開一個已存在的文檔:調用CDocument::OnOpenDocument打開指定的文件;執行DeleteContext,保證文檔為空;調用CObject::Serialize讀入該文件的內容。(程序員可在此進行文件的讀入操作。當然,也可以在CDocument::OnOpenDocument中讀入文件)。然后置文檔為清潔;最后,調用CDocTemplate::SetPathName,并把文件名加入到最近文件列表中。
             
            (4)、調用CDocTemplate::InitialUpdateFrame函數,使框架窗口中的各個視收到OnInitialUpdate調用。框架窗口的主視(子窗ID等于AFX_IDW_PANE_FIRST的視)被激活。程序員可以在此對視對象進行初始化。 

            (2)、Window/New命令的程序流程 

                  當主框架窗口上有子窗口時,選擇Window/New命令可以生成該活動子窗口的影象。它們有相同的文檔模板、相同的文檔。其流程如下: 
              
                  執行Window/New的過程與File/New的過程差不多。所不同的是,File/New須要創建一個新文檔,而Window/New則是獲得已存在的MDI子窗口的文檔。因此以前存在的視和New以后生成的視均為該文檔的視,都是該文檔的內容的顯示。當調用CDocument::UpdateAllViews函數時,它們(視)的OnUpdate函數都將被激活。此時,在該文檔的視指針列表中,將有多于一個的視(具體數目視Window/New執行的次數而定)。讀者可以利用(圖3)中的代碼跟蹤程序結果。 
              
            (四)、幾種情況的討論 

            上面,筆者就MFC中文檔/視的關系進行了分析,下面,筆者將結合具體情況進行討論: 

            (1)、如何根據自己的要求來選擇文檔模板,及相應的視和文檔。 

                  在通常的MDI應用程序中,只有一個文檔模板,程序員只能打開一種類型的文檔。因此,程序員只要調用File/New或者File/Open創建或者打開文檔即可,至于文檔、視和框架窗口之間的關系,由文檔模板在幕后控制,不須要對文檔模板進行操作。但是,如果應用程序需要處理多種類型的文檔,并且何時打開何種文檔均需程序員手工控制,此時,程序員必須對文檔模板進行編程。 

                  例如,筆者需要處理AVI和BMP兩種文件類型。AVI和BMP的數據存放格式不同,不能用同一的數據結構來描述,因此,把它們的數據都存入一個文檔是不合適的。同時,由于AVI是圖象序列,BMP僅是一幅圖象,它們的顯示是肯定不一樣的,即它門的視不同。基于此,筆者決定分別建立兩套文檔模板,兩套框架窗口,兩套文檔和兩套視,分別用于AVI和BMP的數據存放和顯示。程序可以根據用戶選擇的文件名來分別處理AVI和BMP。具體步驟如下: 

            (Step 1)、在應用程序類(CWinApp)的派生類中增加文檔模板成員變量,以便對文檔模板進行操作。 

            class C3dlcsApp : public CWinApp 

                  ......
            public: 
                  CMultiDocTemplate * m_pAVIDocTemplate; 
                  CMultiDocTemplate * m_pBMPDocTemplate; 


            (Step 2)、在主框架中增加菜單響應: 

            void CMainFrame::OnFileOpen() 

                  CFileDialog my(true); 
                  if(my.DoModal()==IDOK) 
                  { 
                        CString FileName = my.GetPathName(); 
                        CString FileExt = my.GetFileExt(); 
                        if((FileExt == "AVI") || (FileExt == "avi")) 
                        { 
                              CMyApp * pMyApp = (CMyApp *)AfxGetApp(); 
                              CMultiDocTemplate*pAVIDocTemplate=pMyApp->m_pAVIDocTemplate; 
                              pAVIDocTemplate->OpenDocumentFile(FileName); 
                        } 

                        else if((FileExt == "BMP") || (FileExt == "bmp")) 
                        { 
                              CMyApp * p3dlcsApp = (CMyApp *)AfxGetApp(); 
                              CMultiDocTemplate* pDATDocTemplate=pMyApp->m_pBMPDocTemplate; 
                              pDATDocTemplate->OpenDocumentFile(FileName); 
                        } 
                        else 
                        { 
                              AfxMessageBox("Yor select a file not supported!"); 
                              return; 
                        } 
                  } 


                  筆者把用戶輸入文件名的后綴作為分支條件,如果是AVI文件,則先獲得關于AVI文件的文檔模板,然后調用CDocTemplate::OpenUpdateFrame (lpszFileName)函數打開此文檔。正如前面所分析,此函數將依次生成新文檔,新框架,在CMDIChildWnd::OnCreateClient中創建視,最后向框架中所有的視發送初始化消息,使其顯示在屏幕上。如果是BMP文件,操作類似。
             
                  當然,程序員也可以在程序的任何位置實現此操作:通過全局函數AfxGetApp 獲得應用程序對象指針,從而獲得相應的文檔模板指針。
             
                  由于由AppWizard生成的應用程序會缺省調用CWinApp::OnFileNew,所以當程序開始執行時,會在主框架上顯示一個新的空窗口。如果想去掉這個空窗口,只須重載CWinApp::OnFileNew函數,不許要任何代碼,即可。 
              
            (2)、切分窗口與文檔/視結構 

                  一個文檔可以有多個視,切分窗口即是表示多視的一種方法。 切分窗口是通過類CSplitterWnd來表示的,對Window來說,CSplitterWnd對象是一個真正的窗口,它完全占據了框架窗口的客戶區域,而視窗口則占據了切分窗口的窗片區域。切分窗口并不參與命令傳遞機制,(窗片中)活動的視窗從邏輯上來看直接被連到了它的框架窗口中。 

                  切分窗口可以分為動態和靜態兩種。前者較簡單,本文僅討論后者。創建切分窗口的步驟如下: 

            (Step 1)、在自己的框架窗口中聲明成員變量,用以對切分窗口進行操作。 

            class CMyFrame : public CMDIChildWnd 

                  ......
                  CSplitterWnd m_Splitter; 
                  CSplitterWnd m_Splitter2; 


            (Step 2)、重載CMDIChildWnd::OnCreateClient函數,創建切分窗口。 

            BOOL CMyFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) 

                  BOOL btn = m_Splitter.CreateStatic(this,1,2); 
                  btn |= m_Splitter.CreateView(0,0, RUNTIME_CLASS(CAVIDispView), CSize(100,100), pContext); 

                  m_Splitter2.CreateStatic(&m_Splitter, 2, 1, 
                        WS_CHILD | WS_VISIBLE | WS_BORDER, 
                        m_Splitter.IdFromRowCol(0, 1)); 

                  btn |= m_Splitter2.CreateView(0, 0, RUNTIME_CLASS(CBMPView), CSize(100,100), pContext); 
                  btn |= m_Splitter2.CreateView(1, 0, RUNTIME_CLASS(CAVIView), CSize(100,100), pContext); 

                  return btn; 
                  //return CMDIChildWnd::OnCreateClient(lpcs, pContext); 


            CFrameWnd::OnCreateClient函數原形為: 

            virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CcreateContext * pContext); 

                  缺省的CMDIChildWnd::OnCreateClient函數根據pContext參數提供的信息,調用CFrameWnd::CreateView函數創建一個視。可以重載該函數,加載CCreateContext對象中傳遞的值,或改變框架窗口主客戶區中控制的創建方式。在上面的程序中,筆者 創建了3個切分窗口。比如打開了一個名為“a.avi”的文檔,此時該文檔將有3個視,一個框架窗口。如果執行了Window/New操作,則此時有一個文檔,6個視和2個框架窗口。若該文檔調用CDocument::UpdateAllViews函數,則這6個視的CView::OnUpdate函數都會被激發。 

            (3)、關于CCreateContext的討論。 

                  CCreateContext是MFC框架所使用的一種結構,它將構成文檔/視的組件聯系起來。這個結構包括指向文檔的指針,框架窗口,視以及文檔模板,它還包含一個指向CRuntimeClass的指針,以指明所創建的視的類型。其數據成員如下: 

            m_pNewViewClass:指向創建上下文的視的CRuntimeClass的指針。 
            m_pCurrentDoc:指向文檔對象的指針,以和新視聯系起來。 
            m_pNewDocTemplate:指向與框架窗口的創建相聯系文檔模板的指針。 
            m_pLastView:指向已存在的視,它是新產生的視的模型。 
            m_pCurrentFrame:指向已存在的框架窗口,它是新產生的框架窗口的模型。 

                  程序員可以通過改變CCreateContext對象的值,來創建更加靈活的視。由于過程較復雜,筆者不再贅許敘,讀者可參閱相關的Visual C++ Help文檔。 

            (五)、結束語 
                  Visual C++ 6.0的文檔/視結構代表了一種新的程序設計方式,其核心是文檔與視的分離,即數據存放與顯示(操作)的分離。在MFC類庫中,各個對象之間的關系很復雜,但,只要深入了解后,會發現它們之間是相互聯系的,可以相互存取的。如果大家想設計出靈活、健壯的應用程序,就必須深入了解MFC。跟蹤原代碼就是一個較好的方法。文檔/視的關系的確非常復雜,如果能知道每個函數是在哪調用的,執行了何種操作,就能游人刃有余,寫出優美的應用程序。
            Posted on 2005-12-15 13:55 艾凡赫 閱讀(466) 評論(0)  編輯 收藏 引用 所屬分類: MFC技術
            欧美亚洲色综久久精品国产| 四虎国产精品成人免费久久| 久久综合88熟人妻| 狠狠干狠狠久久| 久久青青国产| 久久伊人精品青青草原高清| 久久天天躁狠狠躁夜夜avapp| 色综合久久无码中文字幕| 久久久青草久久久青草| 国产免费久久精品99re丫y| 成人免费网站久久久| 久久笫一福利免费导航 | 久久亚洲高清综合| 无码AV中文字幕久久专区| 久久精品免费大片国产大片| 色综合久久无码中文字幕| 久久综合伊人77777| 国产精品久久久久久福利69堂| 亚洲七七久久精品中文国产| 欧美精品一区二区精品久久| 麻豆一区二区99久久久久| 久久精品国产日本波多野结衣| 久久精品国产国产精品四凭| 久久国产精品99久久久久久老狼 | 久久久久亚洲AV无码网站| 亚洲综合久久夜AV | 久久综合一区二区无码| 久久精品国产黑森林| 国产成人AV综合久久| 久久综合久久综合久久| 精品久久一区二区三区| 国产精品一区二区久久国产| 麻豆成人久久精品二区三区免费 | 一级做a爰片久久毛片人呢| 久久Av无码精品人妻系列| 一本久久知道综合久久| 欧美亚洲国产精品久久| 久久久久久精品无码人妻| 思思久久精品在热线热| 亚洲精品无码成人片久久| 日韩精品久久无码中文字幕|