第三卷 使用文檔/視圖結構(2)(1 / 3)

同時,由於從新位置開始顯示的字符串不再包括在鼠標定位插入符以前鍵入的字符串。我們需要將字符串對象m_TextString清空,然後從m_BeginPoint定義的位置開始顯示後麵輸入的字符。

利用CString類提供的成員函數可以將該字符串對象清空為一個空字符串。該函數的使用很簡單,沒有參數,也沒有返回值。

修改OnLButtonDown函數如下:

程序清單7-18 OnLButtonDown函數

void CSingleDoc1View::OnLButtonDown(UINT nFlags, CPoint point)

{

// TODO: Add your message handler code here and/or call default

HideCaret();

m_BeginPoint=point;

CSingleDoc1Doc* pDoc=GetDocument();

pDoc->m_TextString.Empty();

m_BeginPoint=point;

m_CaretPos=point;

SetCaretPos(m_BeginPoint);

ShowCaret();

CView::OnLButtonDown(nFlags, point);

}

函數首先隱藏插入符,然後將插入符定位到新的位置,最後在新的位置重新顯示插入符。值得一提的是,對於CPoint的對象m_BeginPoint,我們直接使用賦值運算符將point的值傳給了它。

相應地,我們也需要對OnChar函數加以修改。當用戶用鼠標進行插入符的定位後,它需要從新的位置m_BeginPoint開始顯示字符串。並且,當用戶鍵入字符後的插入符定位也要加以改變,用m_BeginPoint加上當前字符串的顯示長度來得到新的插入符位置。修改後OnChar函數的代碼如下:

程序清單7-19 OnChar函數

void CSingleDoc1View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)

{

// TODO: Add your message handler code here and/or call default

HideCaret();

CSingleDoc1Doc* pDoc=GetDocument();

pDoc->m_TextString+=nChar;

CClientDC dc(this);

dc.TextOut(m_BeginPoint.x,m_BeginPoint.y,pDoc->m_TextString);

CSize str_size=dc.GetTextExtent(pDoc->m_TextString);

m_CaretPos.x=m_BeginPoint.x+str_size.cx;

SetCaretPos(m_CaretPos);

ShowCaret();

CView::OnChar(nChar, nRepCnt, nFlags);

}

另外,我們還需要在OnCreate函數中對m_BeginPoint進行初始化,修改OnCreate函數的代碼如下:

程序清單7-20 OnCreate函數

int CSingleDoc1View::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CView::OnCreate(lpCreateStruct) == -1)

return -1;

// TODO: Add your specialized creation code here

CClientDC dc(this);

TEXTMETRIC tm1;

dc.GetTextMetrics(&tm1);

CreateSolidCaret(tm1.tmAveCharWidth/8,tm1.tmHeight);

m_CaretPos.x=0;

m_CaretPos.y=0;

m_BeginPoint=0;

m_BeginPoint=0;

SetCaretPos(m_CaretPos);

ShowCaret();

return 0;

}

現在再運行程序。先在視圖窗口中進行輸入,然後在其它位置上單擊鼠標並繼續進行輸入,程序的運行如圖7-5所示。

圖7-5 用鼠標定位插入符

到現在為止,這個SDI應用程序就全部完成了。下一節我們將結合一個多文檔的編輯器的開發,講述多文檔的應用。

第三章 多文檔應用

在前一節中我們建立了一個單文檔的應用。它能夠進行一些簡單的文本輸入。在這一節裏,我們將建立一個新的項目MultiDoc1。它是一個基於多文本的應用。該項目與前一節中建立的文本編輯器有很多相似之處,當然也有許多不同。最大的不同之處是MultiDoc1可以支持打開多個文檔。

7.3.1 建立一個多文檔的應用

首先我們利用AppWizard建立一個基於多文檔應用的項目MultiDoc1。具體的操作步驟如下:

(1)首先在File菜單中單擊New,單擊Project標簽,選擇MFC AppWizard(exe)。在Project Name框中鍵入MultiDoc1。

(2)單擊OK 進入下一個對話框。選擇基於多文檔的應用,並在Language下拉框選取英語。

注意:

* 有一個Document view architechture support?(支持文檔/視圖結構嗎?)的複選框,單擊它前麵的方框,使其中出現一個勾的記號,表示選中了該項。

(1)單擊Next進入下一對話框,選擇None。

(2)單擊Next進入下一對話框,清除ActiveX Control前麵的複選標誌。

(3)單擊Next進入下一對話框,不要改動係統的缺省設置。

(4)單擊Next進入下一對話框,分別選取"MFC Project ","Yes,please" ,"As a share DLL"項。

(5)單擊 Next,Appwizard 在其中列出了所有將要產生的類及文件信息。單擊Finish。

這樣,我們就建立了一個基於多文檔應用的項目。項目的有關信息如圖7-6所示。

圖7-6 MultiDoc1的項目信息

顯示了將要產生的新項目的一些信息。單擊OK,產生項目MultiDoc1。

7.3.2 分析AppWizard產生的MDI框架程序

我們首先運行一下現在的MDI框架程序。每當選擇一次File/New命令,程序都會打開一個新的文檔窗口,而不是象在SDI中隻是清除了當前的文檔顯示。程序的運行如圖7-7所示。

圖7-7 由AppWizard生成的MultiDoc1程序

下麵我們分析一下AppWizard自動產生的MDI項目的框架,看看與在單文檔應用時有哪些不同。

1.文檔模板

在CMultiDoc1App類的InitInstance函數中(MultiDoc1.cpp),如下定義了多文檔應用的文檔模板:

CMultiDocTemplate* pDocTemplate;

pDocTemplate = new CmultiDocTemplate

(

IDR_MULTIDTYPE,

RUNTIME_CLASS(CMultiDoc1Doc),

RUNTIME_CLASS(CChildFrame), // custom MDI child frame

RUNTIME_CLASS(CMultiDoc1View));

AddDocTemplate(pDocTemplate);

}

就像在第一節見到的CSingleDocTemplate一樣,多文檔模板類CMultiDocTemplate也允許使用多種文檔類型,但是它允許同時存在多個文檔對象,這是MDI應用的最重要的特征。

該文檔模板的作用是將一個框架子窗口、一個文檔類和一個視圖類聯係在一起。這個視圖類依附於框架子窗口。由於CChildFrame已經給傳給了CMultiDocTemplate類的構造函數,應用框架可以動態的創建應用框架子窗口CChildFrame的對象。

另外,MDI應用可以通過多次調用AddDocTemplate函數來支持多個文檔模板,每個模板可以指定不同的文檔類、視圖類和MDI子框架窗口類的組合。當用戶從File菜單中選擇New時,應用框架會顯示一個列表框,讓用戶選擇一個模板。而在SDI應用中不支持多個文檔模板,因為在整個應用期間,文檔、視圖和框架均隻被創建一次。

2.框架窗口及子窗口

在SDI程序中,主窗口CMainFrame由基類框架窗口CFrameWnd派生出來。由於它的文檔模板將視圖、文檔、主框架窗口聯係在一起,每個視圖直接屬於主框架窗口。整個應用隻允許存在一個主框架窗口類和一個主框架窗口對象,因此不允許在程序運行時動態的創建文檔模板,進而創建新的文檔。所以它隻能打開一個文檔。

而在MDI程序中,存在兩種框架窗口:主框架窗口CMainFrame和子框架窗口CChildFrame。主框架窗口與前麵SDI應用中的主框架窗口相同,而子框架窗口則屬於主框架窗口。子框架窗口沒有菜單和工具欄,隻有一個用戶區可以容納視圖窗口。

主窗口CMainFrame是由類CMDIFrameWnd 派生出來的,如下所示:

class CMainFrame : public CMDIFrameWnd

{

DECLARE_DYNAMIC(CMainFrame)

public:

CMainFrame();

...............

它在InitInstance函數中被創建如下:

CMainFrame* pMainFrame = new CMainFrame;

if (!pMainFrame->LoadFrame(IDR_MAINFRAME))

return FALSE;

m_pMainWnd = pMainFrame;

對於該主窗口,它可以擁有多個框架子窗口類。而每個框架子窗口類CChildFrame和一個文檔類、一個視圖類通過文檔模板聯係在一起。視圖和文檔隻屬於框架子窗口,並不依附於主框架窗口類。因此,盡管程序中隻允許有一個一個主框架窗口類和一個主框架窗口對象,但是卻可以存在多個框架子窗口類。因此,隻要在運行時動態的創建新的文檔模板,就可以產生多個框架子窗口,從而允許存在多個文檔對象。正是這種將主框架窗口與視圖窗口分離的辦法使得它可以支持多個文檔。

盡管程序中存在多個框架子窗口,但是任意時刻,隻有一個框架子窗口處於活動狀態。用戶對於主框架窗口中的菜單和工具條的命令和操作被傳給當前活動的框架子窗口。主窗口的標題條中顯示的是活動窗口中的文檔文件名。

3.函數OnFileNew

這個函數對應著菜單中的File/New命令,用於創建空文檔。對於MDI應用來說,每次打開程序時都會自動的打開一個新文檔。因此,InitInstance函數也要調用OnFileNew函數。

在文檔類的文件MultiDoc1Doc.cpp中,包含了函數OnNewDocument的代碼,其代碼如下:

程序清單7-21 函數OnNewDocument的代碼

BOOL CMultiDoc1Doc::OnNewDocument()