当前位置:编程学习 > VC++ >>

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 : interface 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 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,