2009-03-31
第16單元? 多文檔界面程序
本單元教學(xué)目標(biāo)
介紹多文檔界面(MDI)程序的構(gòu)造和編程方法夕吻。
學(xué)習(xí)要求
理解多文檔界面(MDI)程序的構(gòu)造零截,掌握其編程方法。
授課內(nèi)容
和框架窗口界面程序鳖孤、單文檔界面(SDI)程序和基于對(duì)話(huà)框的應(yīng)用程序一樣,多文檔界面(MDI)程序也是基本的MFC應(yīng)用程序結(jié)構(gòu)。MDI程序的結(jié)構(gòu)最復(fù)雜阁簸,功能也最強(qiáng)。其特點(diǎn)是用戶(hù)一次可以打開(kāi)多個(gè)文檔哼丈,每個(gè)文檔均對(duì)應(yīng)不同的窗口启妹;主窗口的菜單會(huì)自動(dòng)隨著當(dāng)前活動(dòng)的子窗口的變化而變化;可以對(duì)子窗口進(jìn)行層疊醉旦、平鋪等各種操作饶米;子窗口可以在MDI主窗口區(qū)域內(nèi)定位、改變大小车胡、最大化和最小化檬输,當(dāng)最大化子窗口時(shí),它將占滿(mǎn)MDI主窗口的全部客戶(hù)區(qū)匈棘。MDI不僅可以在同一時(shí)間內(nèi)同時(shí)打開(kāi)多個(gè)文檔丧慈,還可以為同一文檔打開(kāi)多個(gè)視圖。
16.1???? MDI應(yīng)用程序
從程序員角度看主卫,每個(gè)MDI應(yīng)用程序必須有一個(gè)CMDIFrameWnd或其派生類(lèi)的對(duì)象伊滋,該窗口稱(chēng)作MDI框架窗口。CMDIFrameWnd是CFrameWnd的派生類(lèi)队秩,除了擁有CFrameWnd類(lèi)的全部特性外笑旺,還具有以下與MDI相關(guān)的特性:
1.與SDI不同,MDI的框架窗口并不直接與一個(gè)文檔和視圖相關(guān)聯(lián)馍资。MDI的框架窗口擁有客戶(hù)窗口筒主,在顯示或隱藏控制條(包括工具條、狀態(tài)欄鸟蟹、對(duì)話(huà)條)時(shí)乌妙,重新定位該子窗口。
2.MDI客戶(hù)窗口是MDI子窗口的直接父窗口建钥,它負(fù)責(zé)管理主框架窗口的客戶(hù)區(qū)以及創(chuàng)建子窗口藤韵。每個(gè)MDI主框架窗口都有且只有一個(gè)MDI客戶(hù)窗口。
3.MDI子窗口是CMDIChildWnd或其派生類(lèi)對(duì)象熊经,CMDIChildWnd也是CFrameWnd的派生類(lèi)泽艘,用于容納視圖和文檔欲险,相當(dāng)于SDI下的主框架窗口。每打開(kāi)一個(gè)文檔匹涮,框架就自動(dòng)為文檔創(chuàng)建一個(gè)MDI子窗口天试。一個(gè)MDI應(yīng)用程序負(fù)責(zé)動(dòng)態(tài)的創(chuàng)建和刪除MDI子窗口。在任何時(shí)刻然低,最多只有一個(gè)子窗口是活動(dòng)的(窗口標(biāo)題欄顏色呈高亮顯示)喜每。MDI框架窗口始終與當(dāng)前活動(dòng)子窗口相關(guān)聯(lián),命令消息在傳給MDI框架窗口之前首先分派給當(dāng)前活動(dòng)子窗口雳攘。
4.在沒(méi)有任何活動(dòng)的MDI子窗口時(shí)带兜,MDI框架窗口可以擁有自己的缺省菜單。當(dāng)有活動(dòng)子窗口時(shí)吨灭,MDI框架窗口的菜單條會(huì)自動(dòng)被子窗口的菜單所替代刚照。框架會(huì)自動(dòng)監(jiān)視當(dāng)前活動(dòng)的子窗口類(lèi)型沃于,并相應(yīng)的改變主窗口的菜單涩咖。例如海诲,在Visual Studio中繁莹,當(dāng)選擇對(duì)話(huà)框模板編輯窗口或源程序窗口時(shí),菜單會(huì)有所不同特幔。但是咨演,對(duì)于程序員來(lái)說(shuō),只需在InitInstance()中注冊(cè)文檔時(shí)指定每一類(lèi)子窗口(嚴(yán)格的講是文檔)所使用的菜單蚯斯,而不必顯式的通過(guò)調(diào)用函數(shù)去改變主框架窗口的菜單薄风,因?yàn)榭蚣軙?huì)自動(dòng)完成這一任務(wù)。
5.MDI框架窗口為層疊拍嵌、平鋪遭赂、排列子窗口和新建子窗口等一些標(biāo)準(zhǔn)窗口操作提供了缺省的菜單響應(yīng)。在響應(yīng)新建子窗口命令時(shí)横辆,框架調(diào)用CDocTemplate::CreateNewFrame()為當(dāng)前活動(dòng)文檔創(chuàng)建一個(gè)子窗口撇他。CreateNewFrame()不僅創(chuàng)建子窗口,還創(chuàng)建與文檔相對(duì)應(yīng)的視圖狈蚤。
與開(kāi)發(fā)基于對(duì)話(huà)框的應(yīng)用程序和SDI應(yīng)用程序一樣困肩,使用AppWizard可生成一個(gè)MDI應(yīng)用程序框架,在此基礎(chǔ)上脆侮,程序員可使用ClassWizard和各種資源編輯器來(lái)充實(shí)自己的應(yīng)用程序锌畸。
AppWizard為MDI程序框架創(chuàng)建了以下類(lèi):
?????? 類(lèi)???????????????????????? 說(shuō)??? 明
CAboutDlg??????????? “關(guān)于”對(duì)話(huà)框
CChildFrame???????? 子框架窗口,用于容納視圖
CMyApp?????????????? 應(yīng)用程序類(lèi)
CmyDoc?????????????? 繪圖程序視圖類(lèi)
CMyView????????????? 繪圖視圖類(lèi)
CMainFrame????????? 框架窗口(用來(lái)容納子窗口)靖避,是MDI應(yīng)用程序的主窗口
可以看出潭枣,MDI比SDI多了一個(gè)CchildFrame(子框架窗口)類(lèi)比默,而且CMainFrame的職責(zé)也不同了。
另外卸耘,MDI和SDI的初始化應(yīng)用程序?qū)嵗椒ㄉ弦灿兴煌硕亍DI應(yīng)用程序的InitInstance()函數(shù)代碼為:
BOOL CDrawApp::InitInstance()
{
… … ????????????????????????????????? // 初始化工作
CMultiDocTemplate* pDocTemplate;??????????? // MDI文檔模板
pDocTemplate = new CMultiDocTemplate(
IDR_DRAWTYPE,
RUNTIME_CLASS(CDrawDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CDrawView));
AddDocTemplate(pDocTemplate);
CMainFrame* pMainFrame = new CMainFrame;??? // 建立MDI主框架窗口
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
m_pMainWnd->DragAcceptFiles();????????? // 設(shè)置框架窗口特性
EnableShellOpen();
RegisterShellFileTypes(TRUE);
CCommandLineInfo cmdInfo;?????????????? // 處理命令行
ParseCommandLine(cmdInfo);
if (!ProcessShellCommand(cmdInfo))
return FALSE;
pMainFrame->ShowWindow(m_nCmdShow);???? // 顯示主框架窗口
pMainFrame->UpdateWindow();
return TRUE;
}
注冊(cè)文檔模板時(shí),首先創(chuàng)建一個(gè)CMultiDocTemplate類(lèi)(對(duì)SDI是CSingleDocTemplate)的模板對(duì)象蚣抗,然后用AddDocTemplate()把它加入到文檔模板鏈表中去侈百。
CmultiDocTemplate()構(gòu)造函數(shù)有四個(gè)參數(shù),第1個(gè)參數(shù)是文檔使用的資源ID定義翰铡,第2個(gè)是文檔類(lèi)型钝域,第3個(gè)是子窗口類(lèi)型,第4個(gè)是視圖類(lèi)型锭魔。
與SDI不同例证,由于MDI的主框架窗口并不直接與文檔相對(duì)應(yīng),因此無(wú)法通過(guò)創(chuàng)建文檔來(lái)創(chuàng)建主框架窗口迷捧,而需要自己去創(chuàng)建:
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
[例16-1] 繪圖程序织咧。用戶(hù)可以鼠標(biāo)“拖曳”方式在視圖中畫(huà)直線段,線的粗細(xì)和顏色可調(diào)漠秋。采用MDI結(jié)構(gòu)笙蒙,可同時(shí)打開(kāi)多個(gè)子窗口作圖,所作圖形可以文件形式保存在磁盤(pán)上庆锦。
說(shuō)??? 明:用AppWizard生成一個(gè)MDI程序框架捅位,在第4步打開(kāi)Advanced Options(高級(jí)選項(xiàng))對(duì)話(huà)框,在Document Template Strings(文檔模板字符串)選項(xiàng)卡中將File extension(文件擴(kuò)展名)設(shè)置為“pic”搂抒,即該程序的圖形文件名后綴為“.pic”艇搀。其他選項(xiàng)均使用缺省設(shè)置。
編輯該程序的主菜單求晶,添加一個(gè)“顏色”選項(xiàng)和一個(gè)“寬度菜單”焰雕,其中包括4個(gè)選項(xiàng),其ID和Caption分別設(shè)置為:
?????? ID??????????????????????? Caption???????????????? 命令消息響應(yīng)函數(shù)
?????? ID_COLOR?????????? “顏色”???????????????????? OnColor()
ID_WIDTH1???????? “寬度=1”???????? OnWidth1()
ID_WIDTH3???????? “寬度=3”???????? OnWidth3()
ID_WIDTH5???????? “寬度=5”???????? OnWidth5()
ID_WIDTH7???????? “寬度=7”???????? OnWidth7()
?????? 用Class Wizard在視圖類(lèi)中為上述菜單選項(xiàng)建立相應(yīng)的消息響應(yīng)函數(shù)芳杏,以及幾個(gè)寬度菜單選項(xiàng)相應(yīng)的更新用戶(hù)界面消息函數(shù)矩屁。
?????? 程??? 序:用Class Wizard添加一個(gè)用于描述線段的類(lèi),并為其添加代碼:
?????? ??? class CLine : public CObject?
{
public:
??? CPoint????? m_pointFrom;??????? // 線段起點(diǎn)
??? CPoint????? m_pointTo;????? // 線段終點(diǎn)
??? COLORREF??? m_colorLine;??????????? // 線段顏色
??? int???????? m_nWidth;?????? // 線段寬度
??? CLine(){}??
??? CLine(POINT from, POINT to, COLORREF color, int width);
??? CLine& operator=(CLine& line);
??? void Serialize(CArchive& ar);
??? void DrawLine(CDC *pDC);
??? virtual ~CLine(){}
??? DECLARE_SERIAL(CLine);
};
IMPLEMENT_SERIAL(CLine, CObject, 1)
CLine::CLine(POINT from, POINT to, COLORREF color, int width)
{
??????? m_pointFrom = from;
??? m_pointTo?? = to;
??????? m_colorLine = color;
??? m_nWidth??? = width;
}
CLine& CLine::operator =(CLine& line)
{
??????? m_pointFrom = line.m_pointFrom;
??? m_pointTo?? = line.m_pointTo;
??????? m_colorLine = line.m_colorLine;
??? m_nWidth??? = line.m_nWidth;
??????? return *this;
}
void CLine::Serialize(CArchive &ar)
{
??????? if(ar.IsStoring())
??????? ar << m_pointFrom << m_pointTo << m_colorLine << m_nWidth;
??????? else
??????? ar >> m_pointFrom >> m_pointTo >> m_colorLine >> m_nWidth;
}
void CLine::DrawLine(CDC *pDC)
{
??????? CPen penNew, *ppenOld;
??? penNew.CreatePen(PS_SOLID, m_nWidth, m_colorLine);
??????? ppenOld = pDC->SelectObject(&penNew);
??? pDC->MoveTo(m_pointFrom);
??????? pDC->LineTo(m_pointTo);
??? pDC->SelectObject(ppenOld);
}
修改文檔類(lèi)頭文件蚜锨。在文件首部添加如下代碼:
#include "line.h"
#define MAX_LINES??? 300
并在文檔類(lèi)定義中添加如下數(shù)據(jù)成員:
public:
??????? CLine?? m_lineList[MAX_LINES];
??? int???? m_nCount;
然后利用Class Wizard為文檔類(lèi)重載成員函數(shù)DeleteContents()档插。修改該函數(shù)及文檔類(lèi)的Serialize()函數(shù):
??????? void CMyDoc::DeleteContents()
{
??? m_nCount = 0;??
??? CDocument::DeleteContents();
}
void CMyDoc::Serialize(CArchive& ar)
{
??? if (ar.IsStoring())
??????????????? ar << m_nCount;
??? else
??????????????? ar >> m_nCount;
??? for(int i=0; i<m_nCount; i++)
??????????????? m_lineList[i].Serialize(ar);
}
修改視圖類(lèi)頭文件,在視圖類(lèi)定義中添加如下數(shù)據(jù)成員:
public:
?????? COLORREF?? m_colorCurr;???????? // 當(dāng)前顏色
?????? int????????????????? m_nCurrWidth;????? // 當(dāng)前線寬
?????? BOOL??????????? m_bCaptured;??????? // 是否按下鼠標(biāo)左鍵
?????? CPoint??????????? m_pointFrom;??????? // 當(dāng)前線始端
?????? CPoint??????????? m_pointTo;??????????? // 當(dāng)前線終端
并在視圖類(lèi)構(gòu)造函數(shù)中加入相應(yīng)的初始化代碼:
CMyView::CMyView(): m_pointFrom(0,0), m_pointTo(0,0)
{
????????????? m_bCaptured?? = FALSE;
?????? m_colorCurr? = RGB(255, 0, 0);
????????????? m_nCurrWidth = 3;
}
然后修改視圖類(lèi)的OnDraw()函數(shù)和消息響應(yīng)函數(shù):
void CMyView::OnDraw(CDC* pDC)
{
??? CMyDoc* pDoc = GetDocument();
??? ASSERT_VALID(pDoc);
??? for(int i=0; i<pDoc->m_nCount; i++)
??????????????? pDoc->m_lineList[i].DrawLine(pDC);
}
void CMyView::OnLButtonDown(UINT nFlags, CPoint point)
{
??? CView::OnLButtonDown(nFlags, point);
??? m_pointFrom = m_pointTo = point;
??? SetCapture();
??? m_bCaptured = TRUE;
}
void CMyView::OnLButtonUp(UINT nFlags, CPoint point)
{
??? CView::OnLButtonUp(nFlags, point);
??? if(m_bCaptured)
??? {
??????????????? CMyDoc* pDoc = GetDocument();
??????? ASSERT_VALID(pDoc);
??????? m_pointTo = point;
??????? m_bCaptured = FALSE;
??????????????? ReleaseCapture();
??????? pDoc->m_lineList[pDoc->m_nCount] = CLine(m_pointFrom,m_pointTo,
m_colorCurr, m_nCurrWidth);
??????? pDoc->m_nCount++;
??????????????? pDoc->SetModifiedFlag();
??????? pDoc->UpdateAllViews(this);
??????? Invalidate();
??? }
}
void CMyView::OnMouseMove(UINT nFlags, CPoint point)
{
??? CView::OnMouseMove(nFlags, point);
??? if(m_bCaptured)
??? {
??????????????? CClientDC dc(this);
??????? dc.SetROP2(R2_NOT);
??????????????? dc.MoveTo(m_pointFrom);
??????? dc.LineTo(m_pointTo);
??????????????? m_pointTo = point;?????
??????? dc.MoveTo(m_pointFrom);
??????????????? dc.LineTo(m_pointTo);
??? }
}
void CMyView::OnColor()
{
??? CColorDialog dlg(m_colorCurr);
??? if(dlg.DoModal() == IDOK)
??????????????? m_colorCurr = dlg.GetColor();
}
void CMyView::OnWidth1()
{
??? m_nCurrWidth = 1;??
}
void CMyView::OnWidth3()
{
??? m_nCurrWidth = 3;
}
void CMyView::OnWidth5()
{
??? m_nCurrWidth = 5;
}
void CMyView::OnWidth7()
{
??? m_nCurrWidth = 7;
}
void CMyView::OnUpdateWidth1(CCmdUI* pCmdUI)
{
??? pCmdUI->SetCheck(m_nCurrWidth == 1);
}
void CMyView::OnUpdateWidth3(CCmdUI* pCmdUI)
{
??? pCmdUI->SetCheck(m_nCurrWidth == 3);
}
void CMyView::OnUpdateWidth5(CCmdUI* pCmdUI)
{
??? pCmdUI->SetCheck(m_nCurrWidth == 5);
}
void CMyView::OnUpdateWidth7(CCmdUI* pCmdUI)
{
??? pCmdUI->SetCheck(m_nCurrWidth == 7);
}
輸入輸出:在窗口客戶(hù)區(qū)按下鼠標(biāo)左鍵后移動(dòng)鼠標(biāo)(“拖曳”)可顯示一條始端不變亚再、終端移動(dòng)的黑色細(xì)線段郭膛,放松鼠標(biāo)按鍵后,該線變?yōu)轭A(yù)先確定的顏色和粗細(xì)氛悬。使用菜單選項(xiàng)可改變線段的顏色和粗細(xì)(圖16-1)则剃。
圖16-1 繪圖程序
分??? 析:為了存儲(chǔ)所繪圖形耘柱,自定義的線段類(lèi)CLine應(yīng)可序列化。為此棍现,CLine類(lèi)包含了一個(gè)沒(méi)有參數(shù)的構(gòu)造函數(shù)调煎、一個(gè)重載的賦值運(yùn)算符、Serialize()成員函數(shù)己肮,以及DECLARE_SERIAL()宏和IMPLEMENT_SERIAL宏士袄。
在文檔類(lèi)的Serialize()成員函數(shù)中調(diào)用了CLine類(lèi)的Serialize()函數(shù)。
所有的鼠標(biāo)消息和菜單消息響應(yīng)函數(shù)均在視圖類(lèi)中谎僻。其中用到了CDC類(lèi)的成員函數(shù)SetROP2()在移動(dòng)鼠標(biāo)期間將繪圖模式設(shè)置為異或娄柳,該模式下有兩個(gè)特點(diǎn),一是第2次畫(huà)同一條線可恢復(fù)背景色(擦除)艘绍,一是無(wú)論背景色和畫(huà)線色的設(shè)置如何赤拒,均可保證所畫(huà)線段可見(jiàn),因此特別適合繪制變化圖形诱鞠。
但是挎挖,由于用戶(hù)的鼠標(biāo)可以在屏幕上任意移動(dòng)。當(dāng)鼠標(biāo)移出窗口外時(shí)航夺,窗口無(wú)法收到鼠標(biāo)消息蕉朵。此時(shí),如果松開(kāi)了鼠標(biāo)左鍵敷存,應(yīng)用程序由于無(wú)法接受到該條消息而不會(huì)終止當(dāng)前筆劃墓造,這樣就造成了錯(cuò)誤堪伍。如何避免這種情況發(fā)生呢锚烦?解決的辦法是要讓窗口在鼠標(biāo)移出窗口外時(shí)仍然能接受到鼠標(biāo)消息。幸好帝雇,Windows提供了一個(gè)API函數(shù)SetCapture()解決了這一問(wèn)題涮俄。
OnLButtonDown()中調(diào)用SetCapture()用于捕獲鼠標(biāo),無(wú)論鼠標(biāo)光標(biāo)位置在何處尸闸,都會(huì)將鼠標(biāo)消息送給調(diào)用它的那一個(gè)窗口彻亲。在用完后,需要用ReleaseCapture()釋放窗口對(duì)鼠標(biāo)的控制吮廉,否則其他窗口將無(wú)法接收到鼠標(biāo)消息(在OnLButtonUp()函數(shù)中)苞尝。這樣,即使用戶(hù)在“拖曳”鼠標(biāo)時(shí)越出當(dāng)前窗口的客戶(hù)區(qū)宦芦,也不會(huì)發(fā)生錯(cuò)誤宙址。
OnLButtonUp()函數(shù)中的語(yǔ)句
UpdateAllViews(this);
用于通知所有的視圖數(shù)據(jù)已更新,這是MDI的特別用法调卑。
OnUpdateWidth1()等函數(shù)用來(lái)在相應(yīng)菜單選項(xiàng)前加上被選擇標(biāo)記抡砂。
自學(xué)內(nèi)容
16.2 滾動(dòng)視圖
到目前為止大咱,我們接觸到的程序的窗口客戶(hù)區(qū)均受限于計(jì)算機(jī)屏幕的大小,而這對(duì)某些應(yīng)用來(lái)說(shuō)是不方便的注益。例如碴巾,若要開(kāi)發(fā)“所見(jiàn)即所得”的文本編輯或圖形編輯程序,就希望屏幕上出現(xiàn)的文字或圖形與打印機(jī)的輸出大小接近丑搔。在Windows程序中厦瓢,這可以通過(guò)“滾動(dòng)視圖”技術(shù)實(shí)現(xiàn),即在窗口客戶(hù)區(qū)的右方和下方分別添加一個(gè)垂直滾動(dòng)條和一個(gè)水平滾動(dòng)條啤月,使客戶(hù)區(qū)變?yōu)橐粋€(gè)更大的虛擬客戶(hù)區(qū)的觀察窗口旷痕。
MFC提供了CScrollView類(lèi)來(lái)實(shí)現(xiàn)滾動(dòng)視圖。CScrollView類(lèi)自動(dòng)處理滾動(dòng)條消息并“滾動(dòng)”客戶(hù)區(qū)畫(huà)面顽冶。因此利用CScrollView類(lèi)顯示文檔時(shí)欺抗,可以不必理會(huì)客戶(hù)區(qū)的實(shí)際大小,只要將其當(dāng)作一張很大的輸出平面即可强重。CScrollView類(lèi)的OnPrepareDC()成員函數(shù)會(huì)根據(jù)水平和垂直滾動(dòng)條的位置自動(dòng)設(shè)定DC原點(diǎn)绞呈。因此也可以說(shuō),客戶(hù)區(qū)的左上角坐標(biāo)其實(shí)是水平间景、垂直滾動(dòng)條的位置佃声,而兩個(gè)滾動(dòng)條的活動(dòng)范圍(scroll size)就是虛擬客戶(hù)區(qū)的大小。
CScrollView類(lèi)有以下重要成員函數(shù)實(shí)現(xiàn)上述功能:
1.設(shè)置虛擬客戶(hù)區(qū)的大刑纫:
????????????? void CScrollView::SetScrollSizes ( int nMapMode, SIZE sizeTotal,
const SIZE& sizePage = sizeDefault, const SIZE& sizeLine = sizeDefault );
其中參數(shù)nMapMode為映射模式圾亏,可參看10.3:“GDI坐標(biāo)系”。參數(shù)sizeTotal是整個(gè)虛擬客戶(hù)區(qū)的大蟹馀 志鹃;sizePage是每次“翻頁(yè)”時(shí)的滾動(dòng)量,也就是用戶(hù)按下滾動(dòng)條把柄時(shí)的滾動(dòng)量泽西。sizeLine是每次跳一小格的滾動(dòng)量曹铃,以上參數(shù)的單位均為邏輯坐標(biāo)的單位。
?????? 2.取滾動(dòng)條坐標(biāo)(客戶(hù)區(qū)左上角坐標(biāo))和虛擬客戶(hù)區(qū)大信跎肌:
????????????? CPoint CScrollView::GetScrollPosition() const;
????????????? CSize CScrollView::GetTotalSize() const;
?????? 3.將客戶(hù)區(qū)左上角滾動(dòng)到指定坐標(biāo)
????????????? void CScrollView::ScrollToPosition(POINT pt);
其中參數(shù)pt為指定的客戶(hù)區(qū)坐標(biāo)陕见。
?????? 4.取滾動(dòng)位置和虛擬客戶(hù)區(qū)大小的物理坐標(biāo)
????????????? CPoint CScrollView::GetDeviceScrollPosition()const;
????????????? void CScrollView::GetDeviceScrollSizes(int& nMapMode, SIZE& sizeTotal,
???????????????????? SIZE& sizePage, SIZE& sizeLine)const;
要使應(yīng)用程序支持卷滾,可在用AppWizard生成框架程序時(shí)就指定視圖的基類(lèi)為CSrollView味抖。做法是在AppWizard的MFC AppWizard-Step 6 of 6對(duì)話(huà)框中评甜,在應(yīng)用程序所包含的類(lèi)中選擇視圖類(lèi),然后在Base Class下拉列表框中選擇應(yīng)用程序視圖類(lèi)的基類(lèi)為CScrollView仔涩。
如果要手工修改視圖類(lèi)的基類(lèi)為CScrollView忍坷,可按以下步驟操作:
1.修改視圖類(lèi)所對(duì)應(yīng)的頭文件,將所有用到CView的地方改為CScrollView〕邢唬可以使用文本替換對(duì)話(huà)框(在編輯菜單中)中的替換功能蓖乘,進(jìn)行全局替換。
2.確定虛擬客戶(hù)區(qū)的大小韧骗。這項(xiàng)工作通常在視圖派生類(lèi)的OnInitialUpdate()成員函數(shù)或OnCreate()成員函數(shù)中通過(guò)調(diào)用SetScrollSizes()成員函數(shù)來(lái)完成嘉抒。
3.如果在視圖類(lèi)的消息響應(yīng)函數(shù)(如鼠標(biāo)消息函數(shù))中使用了CClientDC設(shè)備,則要注意該設(shè)備仍以實(shí)際客戶(hù)區(qū)的左上角為原點(diǎn)袍暴,在存儲(chǔ)有關(guān)數(shù)據(jù)時(shí)些侍,可能要將其轉(zhuǎn)換為虛擬客戶(hù)區(qū)坐標(biāo)。轉(zhuǎn)換方法很簡(jiǎn)單政模,只要將坐標(biāo)值加上客戶(hù)區(qū)左上角在虛擬客戶(hù)區(qū)的坐標(biāo)值即可岗宣。然而,OnDraw()函數(shù)無(wú)需任何修改淋样,因其使用的設(shè)備由CScrollView自動(dòng)維護(hù)耗式。
16.3 對(duì)話(huà)視圖
?????? 對(duì)于人事檔案管理、名片管理趁猴、圖書(shū)管理這類(lèi)應(yīng)用程序刊咳,視圖的主要作用是顯示各項(xiàng)文檔資料,同時(shí)又要提供方便的修改手段儡司。如例15-1娱挨,采用對(duì)話(huà)框編輯檔案材料比較方便。然而捕犬,基于對(duì)話(huà)框的應(yīng)用程序不提供文檔類(lèi)跷坝,數(shù)據(jù)存取不方便。
?????? MFC提供了CFormView類(lèi)碉碉,該類(lèi)成員兼有對(duì)話(huà)框和視圖的特點(diǎn)柴钻,最適合作文檔管理類(lèi)應(yīng)用程序的用戶(hù)界面。
要使應(yīng)用程序支持CFormView類(lèi)誉裆,可在用AppWizard生成框架程序時(shí)就指定視圖的基類(lèi)為CFormView顿颅。做法是在AppWizard的MFC AppWizard-Step 6 of 6對(duì)話(huà)框中缸濒,在應(yīng)用程序所包含的類(lèi)中選擇視圖類(lèi)足丢,然后在Base Class下拉列表框中選擇應(yīng)用程序視圖類(lèi)的基類(lèi)為CFormView。
?????? 設(shè)計(jì)CFormView的對(duì)話(huà)框模板方法與設(shè)計(jì)一般對(duì)話(huà)框模板的方法完全相同庇配。
16.4 文本編輯視圖
?????? CEditView類(lèi)是CView類(lèi)的派生類(lèi)斩跌,內(nèi)含一個(gè)CEdit對(duì)象,具有很強(qiáng)的文字編輯功能捞慌,包括剪貼、搜索替換和打印等功能。
?????? CEditView類(lèi)本身有存放文本的內(nèi)存火邓,有序列化功能,幾乎不用任何編程工作便可成為一個(gè)功能相當(dāng)強(qiáng)大的文本編輯器程序氮帐。
?????? 如果要對(duì)CEditView中的CEdit對(duì)象進(jìn)行操作,可使用成員函數(shù):
????????????? CEdit& CEditView::GetEditCtrl()const;
取得該CEdit對(duì)象洛姑。
?????? 要說(shuō)明的是上沐,CEditView類(lèi)只能編輯長(zhǎng)度小于64K的文本文件。
[例16-2] 文本編輯器程序楞艾。該程序的功能與Windows的記事本程序類(lèi)似参咙,但是一個(gè)MDI程序。
說(shuō)??? 明:用AppWizard建立一個(gè)MDI程序硫眯,在第4步設(shè)置文件名后綴(File extension)為“txt”蕴侧,在第6步將視圖類(lèi)的基類(lèi)設(shè)置為CEditView。
注意两入,經(jīng)上述步驟生成的程序直接編譯净宵、聯(lián)接后就已經(jīng)是一個(gè)可以使用的文本編輯器了。下面步驟僅是為該程序添加一個(gè)字體選擇對(duì)話(huà)框裹纳。
?????? 編輯子窗口菜單塘娶,在“文件”菜單中添加一個(gè)選項(xiàng)“字體”,標(biāo)識(shí)符為ID_FONT痊夭,并使用ClassWizard在視圖類(lèi)中為其添加相應(yīng)的消息響應(yīng)函數(shù)OnFont()刁岸。
?????? 程??? 序:首先在視圖類(lèi)中添加一個(gè)字體類(lèi)的數(shù)據(jù)成員:
????????????? public:
?????? CFont m_fontEdit;
?????? 然后編輯OnFont()函數(shù):
??????? void CMy1504View::OnFont()
{
??? CFontDialog dlg;
??? LOGFONT logfont;
??? if(dlg.DoModal() == IDOK)
??? {
??????????????? dlg.GetCurrentFont(&logfont);
??????? textcolor = dlg.GetColor();
??????? m_fontEdit.DeleteObject();
??????? m_fontEdit.CreateFontIndirect(&logfont);
??????? GetEditCtrl().SetFont(&m_fontEdit);
??? }
}
?????? 輸入輸出:可編輯文本文件,具有存儲(chǔ)她我、打印和各種剪貼板操作功能(圖16-2)虹曙。
?????? 分??? 析:通過(guò)字體選擇公用對(duì)話(huà)框選擇字體并創(chuàng)建該字體,然后用CEdit類(lèi)的成員函數(shù)SetFont()設(shè)置字體番舆。
圖16-2 文本編輯器程序
調(diào)試技術(shù)
16.5 使用AppWizard建立MDI程序框架
?????? 使用AppWizard建立MDI程序的方法與建立SDI程序的方法基本相同酝碳,只是在第1步時(shí)選擇Multiple Documents即可。
?????? 另外恨狈,如果是創(chuàng)建MDI項(xiàng)目疏哗,則在AppWizard的第4步中的高級(jí)選項(xiàng)(Advanced? Options)對(duì)話(huà)框中的Window styles選項(xiàng)卡中可以控制文檔窗口的外貌。在該選項(xiàng)卡的下方有一個(gè)MDI child frame styles組框禾怠,其中包括5個(gè)復(fù)選框:
????????????? Thick Frame????????? 粗邊框
????????????? Minimize box???????? 子框架窗口右上角的最小化按鈕
????????????? Maximize box??????? 子框架窗口右上角的最大化按鈕
????????????? Minimized????????????? 子框架窗口最小化
????????????? Maximized???????????? 子框架窗口最大化
程序設(shè)計(jì)舉例
?????? [例16-3] 改寫(xiě)例16-1的繪圖程序返奉,使之使用滾動(dòng)視圖,同時(shí)選用合適的映射模式吗氏,使程序具有“所見(jiàn)即所得”的打印風(fēng)格芽偏。
?????? 按例16-1前面的說(shuō)明用AppWizard建立程序框架并編輯其菜單資源,只是在AppWizard的第6步將視圖類(lèi)的基類(lèi)設(shè)置為CScrollView類(lèi)弦讽。
?????? 程??? 序:CLine類(lèi)和文檔類(lèi)的代碼與例16-1相同污尉。
?????? 視圖類(lèi)的定義及其構(gòu)造函數(shù)、OnColor()函數(shù)和OnDraw()函數(shù)與例16-1相同。用ClassWizard為視圖類(lèi)重載OnInitUpdate()函數(shù)和OnPrepareDC()函數(shù)被碗。其他需修改的成員函數(shù)的代碼如下:
??????? void CMy1603View::OnWidth1()
{
??? m_nCurrWidth = 15;?
}
void CMy1603View::OnUpdateWidth1(CCmdUI* pCmdUI)
{
??? pCmdUI->SetCheck(m_nCurrWidth == 15);
}
void CMy1603View::OnWidth3()
{
??? m_nCurrWidth = 30;?
}
void CMy1603View::OnUpdateWidth3(CCmdUI* pCmdUI)
{
??? pCmdUI->SetCheck(m_nCurrWidth == 30);
}
void CMy1603View::OnWidth5()
{
??? m_nCurrWidth = 60;?
}
void CMy1603View::OnUpdateWidth5(CCmdUI* pCmdUI)
{
??? pCmdUI->SetCheck(m_nCurrWidth == 60);
}
void CMy1603View::OnWidth7()
{
??? m_nCurrWidth = 120;
}
void CMy1603View::OnUpdateWidth7(CCmdUI* pCmdUI)
{
??? pCmdUI->SetCheck(m_nCurrWidth == 120);
}
void CMy1603View::OnColor()
{
??? CColorDialog dlg(m_colorCurr);
??? if(dlg.DoModal() == IDOK)
??????? m_colorCurr = dlg.GetColor();
}
void CMy1603View::OnLButtonDown(UINT nFlags, CPoint point)
{
??? CScrollView::OnLButtonDown(nFlags, point);
??? CClientDC dc(this);
??? OnPrepareDC(&dc);
??? dc.DPtoLP(&point);
??? m_pointFrom = m_pointTo = point;
??? SetCapture();
??? m_bCaptured = TRUE;
}
void CMy1603View::OnLButtonUp(UINT nFlags, CPoint point)
{
??? CScrollView::OnLButtonUp(nFlags, point);
??? if(m_bCaptured)
??? {??
??????????????? CMy1603Doc* pDoc = GetDocument();
??????? ASSERT_VALID(pDoc);
??????????????? CClientDC dc(this);
??????? OnPrepareDC(&dc);
??????? dc.DPtoLP(&point);
??????????????? m_pointTo = point;
??????? m_bCaptured = FALSE;
??????????????? ReleaseCapture();
??????? CPoint org = GetScrollPosition();
??????????????? pDoc->m_lineList[pDoc->m_nCount] = CLine(m_pointFrom+org,
??????????? m_pointTo+org, m_colorCurr, m_nCurrWidth);
??????????????? pDoc->m_nCount++;
??????? pDoc->SetModifiedFlag();
??????????????? pDoc->UpdateAllViews(this);
??????? Invalidate();
??? }
}
void CMy1603View::OnMouseMove(UINT nFlags, CPoint point)
{
??? CScrollView::OnMouseMove(nFlags, point);
??? if(m_bCaptured)
??? {
??? ??????????? CClientDC dc(this);
??????? OnPrepareDC(&dc);
??????? dc.DPtoLP(&point);
??????????????? dc.SetROP2(R2_NOT);
??????? dc.MoveTo(m_pointFrom);
??????????????? dc.LineTo(m_pointTo);
??????? m_pointTo = point;?????
??????????????? dc.MoveTo(m_pointFrom);
??????? dc.LineTo(m_pointTo);
??? }
}
void CMy1603View::OnInitialUpdate()
{
??? CScrollView::OnInitialUpdate();
??? SetScrollSizes(MM_TWIPS, CSize(11520, 15120));
}
void CMy1603View::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
??? pDC->SetMapMode(MM_TWIPS);?
??? CScrollView::OnPrepareDC(pDC, pInfo);
}
?????? 輸入輸出:與例16-1基本相同某宪,但使用了滾動(dòng)視圖,可在虛擬的客戶(hù)區(qū)中作圖锐朴。打印比例適當(dāng)缩抡,在A4紙上可做到“所見(jiàn)即所得”。
?????? 分??? 析:CScrollView需要預(yù)先設(shè)置虛擬客戶(hù)區(qū)的大小等參數(shù)包颁,這一工作在其成員函數(shù)OnInitUpdate()中進(jìn)行瞻想。
?????? 為了實(shí)現(xiàn)“所見(jiàn)即所得”的打印效果,本程序使用了MM_TWIPS邏輯映射模式娩嚼。映射模式在視圖類(lèi)的OnprepareDC()成員函數(shù)中設(shè)置蘑险。
?????? MM_TWIPS映射模式的單位為1/1440英寸。設(shè)置線寬時(shí)應(yīng)以此為標(biāo)準(zhǔn)岳悟。由于鼠標(biāo)消息中的位置參數(shù)是基于物理坐標(biāo)的佃迄,所以在鼠標(biāo)消息響應(yīng)函數(shù)中調(diào)用CDC類(lèi)的成員函數(shù)LPtoDP()將其轉(zhuǎn)換為邏輯坐標(biāo)。由于這些坐標(biāo)是相對(duì)于實(shí)際客戶(hù)區(qū)左上角的坐標(biāo)贵少,而文檔中存儲(chǔ)的應(yīng)是相對(duì)于虛擬客戶(hù)區(qū)左上角的坐標(biāo)呵俏,所以在OnLButtonUp()函數(shù)中還要在這些坐標(biāo)上加上實(shí)際客戶(hù)區(qū)左上角相對(duì)于虛擬客戶(hù)區(qū)左上角的坐標(biāo)方可存入數(shù)組。