第十四章 單文檔界面的程序
用VC++的AppWizard生成的程序有三種樣式的界面,它們是單文檔、多文檔和對話框界面。前面各章我們都生成對話框樣式的界面,這可以方便我們學習各種常用控件的設計,本章要學習如何生成一個單文檔界面的應用程序,以及文檔類和視圖類的概念和相互關係。
本章要點
1 用AppWizard生成單文檔界面的操作方法
2 文檔類和視圖類的概念
3 如何保持文檔和視圖的同步關聯。
14.1 生成單文檔界面的文本編輯器
用AppWizard生成單文檔界面的操作方法和生成對話框界面的操作方法稍有不同,我們通過建立一個“文本編輯器”應用程序MyNote.exe來演示,具體過程如下:
1 打開應用程序嚮導,在d:\myVC\CH14創建應用程序MyNote。
2 在Step1中選擇“單文檔”和“文檔/視圖支持”,如圖chap14-01.
3 在step2和step3對話框中都用默認的。在step4中如圖chap14-02選擇前五項,當然也可以按你的需要選擇。
圖chap14-02
4 在Step5中選擇MFC標準、加備註和靜態連接庫。圖chap14-03
圖chap14-03
5 在step6中選擇視圖的基類(base class),因爲我們需要的是文本編輯器,所以要選類CEditView作爲基類。點擊[完成]按紐,應用程序的框架就創建好了。
14.2 文檔類和視圖類的概念
嚮導工作完成以後馬上編譯和連接,運行後就可以看到一個已經能夠使用的文本編輯器,它不僅可以輸入文字,還可以保存或打開文件,與Windows的記事本差不多了,只是沒有設置字體的功能。圖chap14-04是它運行時的窗口。
菜單和工具欄的響應函數都已經生成了相應的代碼,我們沒有做任何編寫工作就得到了一個“完整”的應用程序,甚至還創建了一個幫助文件呢。這就是VC++開發環境的巨大威力!我們從上圖中可以看到,編輯菜單中的複製和剪切都已經有效了。
圖中的窗口是主框架,對應於類CMainFrame,而其中的“編輯區域”叫用戶區,對應於視圖類CMyNoteView,而被編輯的文本對應於一個文檔類CMyNoteDoc。另外,每個程序還有一個對應於應用程序本身的類,本例中是CMyNoteApp。這四個類就是嚮導自動生成的應用程序框架類,打開工作區的類classView頁區,就可以看到,如圖chap14-05。
展開視圖類,可以找到一個函數GetDocument(),它是使視圖(的內容)和文檔保持同步關聯的紐帶。它返回一個CMyNoteDoc類型的對象指針m_pDocument。使用它在視圖中訪問文檔的內容。
在文檔類中有一個OnNewDocument()函數,在“新建文件”和程序第一次運行時被調用,它打開一個新文檔供用戶編輯。我們來看一下它的代碼:
BOOL CMyNoteDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
((CEditView*)m_viewList.GetHead())->SetWindowText(NULL);
return TRUE;
}
if語句調用其父類的同名函數,第二個語句設置編輯區的文本爲空白。你試着用“這是一個新文檔”來代替NULL,重新編譯和運行,那每次打開時編輯區會出現這句話。
文檔類中還有一個叫Serialize,把它系列化對象CArchive& ar設置爲按行系列化,這是純文本文件存取時所需要的。下面是它的代碼。
void CMyNoteDoc::Serialize(CArchive& ar)
{
// CEditView contains an edit control which handles all serialization
((CEditView*)m_viewList.GetHead())->SerializeRaw(ar);
}
在下一節中,我們將通過一個例程來演示,如何在文檔對象和視圖對象之間互相訪問和交換數據。
14.3 Circle程序的界面和功能
Circle程序的功能是:用鼠標左鍵在用戶區域任意的點一下,就以這一點爲圓心畫一個半徑爲50的圓。程序的界面和上例一樣,都是單文檔程序的基本形式麼。但是其客戶區年輸入文字,是畫圖形式的。
下面我們就來製作這一程序。
1 創建工程
打開應用程序嚮導,在d:\myVC\CH14創建應用程序Circle。選擇“單文檔”,後面的應答與上例一樣。不同的是最後一步視圖基類的選擇,不是選CEditView而是選CView。CEditView類是CView類的子類,適合於需要文字編輯的場合。
2 添加變量
畫圓需要與圓心和半徑,爲了文檔和視圖的同步,我們需要給兩者都引進三個變量m_PosX、 m_PosY和m_Reduse。定義要寫在他們的構造函數中:
public:
int m_PosX, m_PosY;
int m_Reduse ;
3 編寫代碼
(1)函數OnDraw(CDC* pDC)的功能是在客戶區畫圖,我們用它以試圖中的變量值來畫圓,代碼如下:
void CCircleView::OnDraw(CDC* pDC)
{
CCircleDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
//Draw a circle
RECT rect;
rect.left= m_PosX - m_Reduse ;
rect.top = m_PosY - m_Reduse ;
rect.right = m_PosX + m_Reduse ;
rect.bottom= m_PosY + m_Reduse ;
pDC->Ellipse(&rect);
}
其中pDC是由參數表傳遞過來的屏幕設備對象,可以畫圖。RECT是表示舉行的數據結構。是畫圖圓需要的參數。
(2)變量m_PosX等的值在初始更新函數OnInitialUpdate()中給定,而值是從文檔的同名函數中傳遞過來的。
CCircleDoc* pDoc = GetDocument();
m_PosX = pDoc->m_PosX ;
m_PosY = pDoc->m_PosY ;
m_Reduse = pDoc->m_Reduse ;
(3)要求當“按下鼠標時畫圓”,就要添加OnLButtonDown函數,其中使用Invalidate(),它會調用函數OnDraw重新畫客戶區,其代碼如下:
void CCircleView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
m_PosX = point.x ;
m_PosY = point.y ;
Invalidate();
CCircleDoc* pDoc = GetDocument();
pDoc->m_PosX = m_PosX;
pDoc->m_PosY = m_PosY;
pDoc->SetModifiedFlag(TRUE);
CView::OnLButtonDown(nFlags, point);
}
變量point對應的是鼠標當前點的位置,傳遞給視圖的變量,然後畫圓;再把它傳遞給文檔,這就保證視圖和文檔數據的一致性。語句
pDoc->SetModifiedFlag(TRUE);
設置文檔已經被修改的標誌,這使得程序在退出時會詢問“是否要保存?”。
(4)在文檔類中有一個函數OnNewDocument(),它是在按“文件/新建”時被調用的。我們用它給出圓的初始值:
m_PosX = 100;
m_PosY = 100;
m_Reduse = 50 ;
需要的代碼就這些了,現在可以編譯和運行之。
思考和練習
1 文檔類應用程序是如何保持視圖和文檔數據的一致性的?
2 在MyNote程序中添加菜單及其代碼,擴展它的功能。例如,增加文字的大小和顏色等。
3 在Circle程序中添加菜單及其代碼,擴展它的功能。例如,可變的半徑,畫不同的圖形等。