?? chap8_3.htm
字號:
<html>
<head>
<title>8.3 繪圖程序</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<meta name="GENERATOR" content="Microsoft FrontPage 3.0">
<link rel="stylesheet" href="../../../cpcw.css"></head>
<body link="#3973DE" alink="#3973DE" background="../../bg.gif">
<div align="center"><center>
<table width="85%" border="0">
<tr bgcolor="#FFFFFF">
<td>
<div align="center"> </div>
<p align="CENTER"><b><font color="red">8.3 繪圖程序</font></b></p>
<p> 在了解GDI的一些基本知識之后,我們就可以著手編寫繪圖程序了。這個繪圖程序可以讓讀者用鼠標(biāo)器在窗口內(nèi)任意涂寫,并可以保存所畫的內(nèi)容。這里我們參考了Visual
C++的例子Scribble,并作了一些修改和簡化。</p>
<p> 8.3.1 MDI應(yīng)用程序框架</p>
<p> 首先用AppWizard生成繪圖程序的基本框架:</p>
<p> 選擇File->New,彈出New對話框,選擇MFC
AppWizard(exe),并指定項目文件名為Draw。</p>
<p> 在MFC
AppWizard-Step1對話框中指定框架類型為Multiple
Document(多文檔,這是缺省設(shè)置)。</p>
<p> Step2,3按缺省值。在MFC AppWizard Step 4 of 6對話框中,點“Advanced...”按鈕,彈出Advanced
Options對話框。在File Extension編輯框中指定文件名后綴為.drw,按OK關(guān)閉Advanced
Options對話框。</p>
<p> Step5按缺省設(shè)置。在MFC
AppWizard Step 6 of 6中,在應(yīng)用程序所包含的類列表中選擇CDrawView,并為其指定基類為CScrollView,因為繪圖程序需要卷滾文檔。現(xiàn)在點Finish按鈕生成繪圖所需的應(yīng)用程序框架。</p>
<p> 在往框架里添加代碼實現(xiàn)繪圖程序之前,先看看多文檔框架與單文檔框架的差別。</p>
<p> AppWizard為多文檔框架創(chuàng)建了以下類:
</p>
<p> CAboutDlg:“關(guān)于”對話框</p>
<p> CChildFrame:子框架窗口,用于容納視圖</p>
<p> CDrawApp:應(yīng)用程序類</p>
<p> CDrawDoc:繪圖程序視圖類</p>
<p> CDrawView:繪圖視圖類</p>
<p> CMainFrame:主框架窗口,用來容納子窗口,它是多文檔應(yīng)用程序的主窗口。</p>
<p> 在生成的類上,MDI比SDI多了一個CChildFrame子框架窗口類,而且CMainFrame的職責(zé)也不同了。</p>
<p> 另外,MDI和SDI在初始化應(yīng)用程序?qū)嵗弦灿兴煌DI應(yīng)用程序InitInstance函數(shù)如清單8.2定義。</p>
<p> <b>清單</b><b>8.2
多文檔程序的InitInstance成員函數(shù)定義</b></p>
<p>BOOL CDrawApp::InitInstance()</p>
<p>{</p>
<p>//一些初始化工作......</p>
<p>// Register the application's document templates. Document templates</p>
<p>// serve as the connection between documents, frame windows and views.</p>
<p>CMultiDocTemplate* pDocTemplate;</p>
<p>pDocTemplate = new CMultiDocTemplate(</p>
<p>IDR_DRAWTYPE,</p>
<p>RUNTIME_CLASS(CDrawDoc),</p>
<p>RUNTIME_CLASS(CChildFrame), // custom MDI child frame</p>
<p>RUNTIME_CLASS(CDrawView));</p>
<p>AddDocTemplate(pDocTemplate);</p>
<p>// create main MDI Frame window</p>
<p>CMainFrame* pMainFrame = new CMainFrame;</p>
<p>if (!pMainFrame->LoadFrame(IDR_MAINFRAME))</p>
<p>return FALSE;</p>
<p>m_pMainWnd = pMainFrame;</p>
<p>// Enable drag/drop open</p>
<p>m_pMainWnd->DragAcceptFiles();</p>
<p>// Enable DDE Execute open</p>
<p>EnableShellOpen();</p>
<p>RegisterShellFileTypes(TRUE);</p>
<p>// Parse command line for standard shell commands, DDE, file open</p>
<p>CCommandLineInfo cmdInfo;</p>
<p>ParseCommandLine(cmdInfo);</p>
<p>// Dispatch commands specified on the command line</p>
<p>if (!ProcessShellCommand(cmdInfo))</p>
<p>return FALSE;</p>
<p>// The main window has been initialized, so show and update it.</p>
<p>pMainFrame->ShowWindow(m_nCmdShow);</p>
<p>pMainFrame->UpdateWindow();</p>
<p>return TRUE;</p>
<p>}</p>
<p>在注冊文檔模板時,首先創(chuàng)建一個CMultiDocTemplate類型(在SDI下是CSingleDocTemplate)的模板對象,然后用AddDocTemplate()把它加入到文檔模板鏈表中去。</p>
<p> CMultiDocTemplate構(gòu)造函數(shù)帶四個參數(shù),第一個參數(shù)是文檔使用的資源ID定義。第二個是文檔類型,第三個是子窗口類型,第四個是視圖類型。</p>
<p> 與SDI不同,由于MDI的主框架窗口并不直接與文檔相對應(yīng),因此無法通過創(chuàng)建文檔來創(chuàng)建主框架窗口,而需要自己去創(chuàng)建。</p>
<p> //定義一個主窗口類指針,并創(chuàng)建一個窗口的空的實例</p>
<p>CMainFrame* pMainFrame = new CMainFrame;</p>
<p>//從資源文件中載入菜單、圖標(biāo)等信息,并創(chuàng)建窗口</p>
<p>if (!pMainFrame->LoadFrame(IDR_MAINFRAME))</p>
<p>return FALSE;</p>
<p>//將應(yīng)用程序?qū)ο蟮闹鞔翱谥羔様?shù)據(jù)成員設(shè)為當(dāng)前創(chuàng)建的窗口</p>
<p>m_pMainWnd = pMainFrame;</p>
<p><b> </b></p>
<p><b> </b>8.3.2 設(shè)計繪圖程序的文檔類</p>
<p> Draw需要保存用戶在屏幕上涂寫的每一個筆劃。一副畫由許多筆劃組成,可以把它看作是筆劃組成的鏈表。每一個筆劃可以看作一個對象,它由許多點組成。這樣,我們可以把繪圖文檔的數(shù)據(jù)看作是筆劃對象CStroke組成的鏈表。另外,我們還需要一些數(shù)據(jù)成員表示當(dāng)前畫圖所使用的畫筆和畫筆的寬度。</p>
<p> 修改后的文檔類聲明文件如清單8-1:</p>
<p> <b>清單</b><b>8.3文檔類聲明</b></p>
<p>// DrawDoc.h : interface of the CDrawDoc class</p>
<p>//</p>
<p>/////////////////////////////////////////////////////////////////////////////</p>
<p>#if !defined(AFX_DRAWDOC_H__143330AE_85BC_11D1_9304_444553540000__INCLUDED_)</p>
<p>#define AFX_DRAWDOC_H__143330AE_85BC_11D1_9304_444553540000__INCLUDED_</p>
<p>#if _MSC_VER >= 1000</p>
<p>#pragma once</p>
<p>#endif // _MSC_VER >= 1000</p>
<p> </p>
<p>class CDrawDoc : public CDocument</p>
<p>{</p>
<p>protected: // create from serialization only</p>
<p>CDrawDoc();</p>
<p>DECLARE_DYNCREATE(CDrawDoc)</p>
<p>// Attributes</p>
<p><b> </b></p>
<b>
<p>public:</p>
<p>UINT m_nPenWidth; // current user-selected pen width</p>
<p>CPen m_penCur; // pen created according to</p>
<p>// user-selected pen style (width)</p>
<p>public:</p>
<p>CTypedPtrList<CObList,CStroke*> m_strokeList; </p>
<p>//獲取當(dāng)前使用的畫筆,為視圖所使用</p>
<p>CPen* GetCurrentPen() { return &m_penCur; }</p>
<p>protected:</p>
<p>CSize m_sizeDoc;</p>
<p>public:</p>
<p>CSize GetDocSize() { return m_sizeDoc; }</p>
<p>// Operations</p>
<p>public:</p>
<p>//往鏈表里增加一個筆劃</p>
<p>CStroke* NewStroke();</p>
<p>// Operations</p>
<p>//用于初始化文檔</p>
<p>protected:</p>
<p>void InitDocument();</p>
</b>
<p>// Overrides</p>
<p>// ClassWizard generated virtual function overrides</p>
<p>//{{AFX_VIRTUAL(CDrawDoc)</p>
<p>public:</p>
<p>virtual BOOL OnNewDocument();</p>
<p>virtual void Serialize(CArchive& ar);</p>
<p>//}}AFX_VIRTUAL</p>
<p>// Implementation</p>
<p>public:</p>
<p>virtual ~CDrawDoc();</p>
<p>#ifdef _DEBUG</p>
<p>virtual void AssertValid() const;</p>
<p>virtual void Dump(CDumpContext& dc) const;</p>
<p>#endif</p>
<p>protected:</p>
<p>// Generated message map functions</p>
<p>protected:</p>
<p>//{{AFX_MSG(CDrawDoc)</p>
<p>// NOTE - the ClassWizard will add and remove member functions here.</p>
<p>// DO NOT EDIT what you see in these blocks of generated code !</p>
<p>//}}AFX_MSG</p>
<p>DECLARE_MESSAGE_MAP()</p>
<p>};</p>
<p> </p>
<p> 這里我們使用
指針鏈表模板來保存指向每個筆劃的指針:</p>
<p>CTypedPtrList<CObList,CStroke*> m_strokeList; </p>
<p>其中“<>”第一個參數(shù)表示鏈表基本類型,第二個參數(shù)代表鏈表中所存放的元素的類型。</p>
<p> 為了使用模板,還要修改stdafx.h,在其中加入afxtempl..h頭文件,它包含了使用模板時所需的類型定義和宏:</p>
<p>//.........</p>
<p>#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers</p>
<p>#include <afxwin.h> // MFC core and standard components</p>
<p>#include <afxext.h> // MFC extensions</p>
<p><b> </b></p>
<b>
<p>#include <afxtempl.h> // MFC templates</p>
</b>
<p>#include <afxdisp.h> // MFC OLE automation classes</p>
<p>#ifndef _AFX_NO_AFXCMN_SUPPORT</p>
<p>#include <afxcmn.h> // MFC support for Windows Common Controls</p>
<p>#endif // _AFX_NO_AFXCMN_SUPPORT</p>
<p>//......</p>
<p>由于繪圖程序需要卷滾文檔,因此象前面的編輯那樣,增加一個m_sizeDoc數(shù)據(jù)成員存放文檔的大小。另外,還需要提供一個GetDocSize()來訪問它。NewStroke()用于往鏈表里增加一個筆劃。</p>
<p> 現(xiàn)在,開始設(shè)計CStroke類。筆劃可以看作由一系列點組成,這樣CStroke可以用一個點的數(shù)組來表示。另外,還需要一些成員函數(shù)來訪問這個數(shù)組。我們還希望筆劃能夠自己繪制自己,并用串行化機制保存自己的數(shù)據(jù)。</p>
<p> CStroke類定義清單如8.4,我們把它在CDrawDoc類定義之前。</p>
<p> <b>清單</b><b>8.4
CStroke類定義</b></p>
<p>class CStroke : public CObject</p>
<p>{</p>
<p>public:</p>
<p>CStroke(UINT nPenWidth);//用筆的寬度構(gòu)造一個畫筆</p>
<p> //用于串行化筆劃對象</p>
<p>protected:</p>
<p>CStroke(); //串行化對象所需的不帶參數(shù)的構(gòu)造函數(shù)</p>
<p>DECLARE_SERIAL(CStroke)</p>
<p>// Attributes</p>
<p>protected:</p>
<p>UINT m_nPenWidth; // one pen width applies to entire stroke</p>
<p>public:</p>
<p>//用數(shù)組模板類保存筆劃的所有點</p>
<p>CArray<CPoint,CPoint> m_pointArray; // series of connected
points</p>
<p>//包圍筆劃所有的點的一個最小矩形,關(guān)于它的作用以后會提到</p>
<p>CRect m_rectBounding; // smallest rect that surrounds all</p>
<p>// of the points in the stroke</p>
<p>// measured in MM_LOENGLISH units</p>
<p>// (0.01 inches, with Y-axis inverted)</p>
<p>public:</p>
<p>CRect& GetBoundingRect() { return m_rectBounding; }</p>
<p>//結(jié)束筆劃,計算最小矩形</p>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -