多文檔視圖結(jié)構(gòu)

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ù)組。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末滔灶,一起剝皮案震驚了整個(gè)濱河市普碎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌录平,老刑警劉巖麻车,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異斗这,居然都是意外死亡动猬,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)表箭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赁咙,“玉大人,你說(shuō)我怎么就攤上這事免钻”怂” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵伯襟,是天一觀的道長(zhǎng)猿涨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)姆怪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮稽揭,結(jié)果婚禮上俺附,老公的妹妹穿的比我還像新娘。我一直安慰自己溪掀,他們只是感情好事镣,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著揪胃,像睡著了一般璃哟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喊递,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天随闪,我揣著相機(jī)與錄音,去河邊找鬼骚勘。 笑死铐伴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俏讹。 我是一名探鬼主播当宴,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼泽疆!你這毒婦竟也來(lái)了户矢?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤殉疼,失蹤者是張志新(化名)和其女友劉穎逗嫡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體株依,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驱证,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恋腕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抹锄。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖荠藤,靈堂內(nèi)的尸體忽然破棺而出伙单,到底是詐尸還是另有隱情,我是刑警寧澤哈肖,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布吻育,位于F島的核電站,受9級(jí)特大地震影響淤井,放射性物質(zhì)發(fā)生泄漏布疼。R本人自食惡果不足惜摊趾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望游两。 院中可真熱鬧砾层,春花似錦、人聲如沸贱案。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宝踪。三九已至侨糟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瘩燥,已是汗流浹背秕重。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留颤芬,地道東北人悲幅。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像站蝠,于是被迫代替她去往敵國(guó)和親汰具。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容