vc++笔记之绘图程序
在了解GDI的一些基本知识之后,我们就可以着手编写绘图程序了。这个绘图程序可以让读者用鼠标器在窗口内任意涂写,并可以保存所画的内容。这里我们参考了Visual C++的例子Scribble,并作了一些修改和简化。
8.3.1 MDI应用程序框架
首先用AppWizard生成绘图程序的基本框架:
选择File->New,弹出New对话框,选择MFC AppWizard(exe),并指定项目文件名为Draw。
在MFC AppWizard-Step1对话框中指定框架类型为Multiple Document(多文档,这是缺省设置)。
Step2,3按缺省值。在MFC AppWizard Step 4 of 6对话框中,点“Advanced...”按钮,弹出Advanced Options对话框。在File Extension编辑框中指定文件名后缀为.drw,按OK关闭Advanced Options对话框。
Step5按缺省设置。在MFC AppWizard Step 6 of 6中,在应用程序所包含的类列表中选择CDrawView,并为其指定基类为CScrollView,因为绘图程序需要卷滚文档。现在点Finish按钮生成绘图所需的应用程序框架。
在往框架里添加代码实现绘图程序之前,先看看多文档框架与单文档框架的差别。
AppWizard为多文档框架创建了以下类:
CAboutDlg:“关于”对话框
CChildFrame:子框架窗口,用于容纳视图
CDrawApp:应用程序类
CDrawDoc:绘图程序视图类
CDrawView:绘图视图类
CMainFrame:主框架窗口,用来容纳子窗口,它是多文档应用程序的主窗口。
在生成的类上,MDI比SDI多了一个CChildFrame子框架窗口类,而且CMainFrame的职责也不同了。
另外,MDI和SDI在初始化应用程序实例上也有所不同。MDI应用程序InitInstance函数如清单8.2定义。
清单8.2 多文档程序的InitInstance成员函数定义
BOOL CDrawApp::InitInstance()
{
//一些初始化工作......
// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows and views.
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_DRAWTYPE,
RUNTIME_CLASS(CDrawDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CDrawView));
AddDocTemplate(pDocTemplate);
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
// Enable drag/drop open
m_pMainWnd->DragAcceptFiles();
// Enable DDE Execute open
EnableShellOpen();
RegisterShellFileTypes(TRUE);
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The main window has been initialized, so show and update it.
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
return TRUE;
}
在注册文档模板时,首先创建一个CMultiDocTemplate类型(在SDI下是CSingleDocTemplate)的模板对象,然后用AddDocTemplate()把它加入到文档模板链表中去。
CMultiDocTemplate构造函数带四个参数,第一个参数是文档使用的资源ID定义。第二个是文档类型,第三个是子窗口类型,第四个是视图类型。
与SDI不同,由于MDI的主框架窗口并不直接与文档相对应,因此无法通过创建文档来创建主框架窗口,而需要自己去创建。
//定义一个主窗口类指针,并创建一个窗口的空的实例
CMainFrame* pMainFrame = new CMainFrame;
//从资源文件中载入菜单、图标等信息,并创建窗口
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
//将应用程序对象的主窗口指针数据成员设为当前创建的窗口
m_pMainWnd = pMainFrame;
8.3.2 设计绘图程序的文档类
Draw需要保存用户在屏幕上涂写的每一个笔划。一副画由许多笔划组成,可以把它看作是笔划组成的链表。每一个笔划可以看作一个对象,它由许多点组成。这样,我们可以把绘图文档的数据看作是笔划对象CStroke组成的链表。另外,我们还需要一些数据成员表示当前画图所使用的画笔和画笔的宽度。
修改后的文档类声明文件如清单8-1:
清单8.3文档类声明
// DrawDoc.h : inte易做图ce of the CDrawDoc class
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_DRAWDOC_H__143330AE_85BC_11D1_9304_444553540000__INCLUDED_)
#define AFX_DRAWDOC_H__143330AE_85BC_11D1_9304_444553540000__INCLUDED_
#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class CDrawDoc : public CDocument
{
protected: // create from serialization only
CDrawDoc();
DECLARE_DYNCREATE(CDrawDoc)
// Attributes
public:
UINT m_nPenWidth; // current user-selected pen width
CPen m_penCur; // pen created according to
// user-selected pen style (width)
public:
CTypedPtrList<CObList,CStroke*> m_strokeList;
//获取当前使用的画笔,为视图所使用
CPen* GetCurrentPen() { return &m_penCur; }
protected:
CSize m_sizeDoc;
public:
CSize GetDocSize() { return m_sizeDoc; }
// Operations
public:
//往链表里增加一个笔划
CStroke* NewStroke();
// Operations
//用于初始化文档
protected:
void InitDocument();
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CDrawDoc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CDrawDoc();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CDrawDoc)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
这里我们使用 指针链表模板来保存指向每个笔划的指针:
CTypedPtrList<CObList,CStroke*> m_strokeList;
其中“<>”第一个参数表示链表基本类型,第二个参数代表链表中所存放的元素的类型。
为了使用模板,还要修改stdafx.h,在其中加入afxtempl..h头文件,它包含了使用模板时所需的类型定义和宏:
//.........
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#include <afxtempl.h> // MFC templates
#include <afxdisp.h> // MFC OLE automation classes
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT
//......
由于绘图程序需要卷滚文档,因此象前面的编辑那样,增加一个m_sizeDoc数据成员存放文档的大小。另外,还需要提供一个GetDocSize()来访问它。NewStroke()用于往链表里增加一个笔划。
现在,开始设计CStroke类。笔划可以看作由一系列点组成,这样CStroke可以用一个点的数组来表示。另外,还需要一些成员函数来访问这个数组。我们还希望笔划能够自己绘制自己,并用串行化机制保存自己的数据。
CStroke类定义清单如8.4,我们把它在CDrawDoc类定义之前。
清单8.4 CStroke类定义
class CStroke : public CObject
{
public:
CStroke(UINT nPenWidth);//用笔的宽度构造一个画笔
//用于串行化笔划对象
protected:
CStroke(); //串行化对象所需的不带参数的构造函数
DECLARE_SERIAL(CStroke)
// Attributes
protected:
UINT m_nPenWidth; // one pen width applies to entire stroke
public:
//用数组模板类保存笔划的所有点
CArray<CPoint,CPoint> m_pointArray; // series of connected points
//包围笔划所有的点的一个最小矩形,关于它的作用以后会提到
CRect m_rectBounding; // smallest rect that surrounds all
// of the points in the stroke
// measured in MM_LOENGLISH units
// (0.01 inches, with Y-axis inverted)
public:
CRect& GetBoundingRect() { return m_rectBoundi
补充:软件开发 , Vc ,