144
第第第第 ActiveX 第第第第第第第第

第十二章 ActiveX 控件的使用和创建

  • Upload
    truly

  • View
    112

  • Download
    0

Embed Size (px)

DESCRIPTION

第十二章 ActiveX 控件的使用和创建. 近年来,软件产业已经发生了一场革命性的变化。软件的制 作和打包方式已经不再是所有的应用程序都必须从源代码编译 链接成一个完整的、很大的可执行代码文件,而是大多数应用 程序都可以由一些较小的 构件 组成。这些小的 构件 ,通常称为 组件 。这些 组件 可以用多种不同的程序语言创建,且可以具有 多种不同的的形式。最为流行的 组件 之一便是 ActiveX 控件 。 组 件 不但可以作为 最终软件产品 提供给其他程序设计人员,而且 在 大型软件开发中 ,使用 组件 也是 组织 不同分工的程序设计人 - PowerPoint PPT Presentation

Citation preview

Page 1: 第十二章  ActiveX  控件的使用和创建

第十二章 ActiveX 控件的使用和创建

Page 2: 第十二章  ActiveX  控件的使用和创建

近年来,软件产业已经发生了一场革命性的变化。软件的制

作和打包方式已经不再是所有的应用程序都必须从源代码编译

链接成一个完整的、很大的可执行代码文件,而是大多数应用

程序都可以由一些较小的构件组成。这些小的构件,通常称为

组件。这些组件可以用多种不同的程序语言创建,且可以具有

多种不同的的形式。最为流行的组件之一便是 ActiveX 控件。组

件不但可以作为最终软件产品提供给其他程序设计人员,而且

在大型软件开发中,使用组件也是组织不同分工的程序设计人

员共同完成整个软件设计开发的重要策略和方法。本章的学习

目的是:

Page 3: 第十二章  ActiveX  控件的使用和创建

掌握如何使用 ActiveX 控件,以便在软件开发中使用第三方提供的产品化组件和如何创建自己的 ActiveX 控件,以便开发产品化组件,提供给其他程序设计者。本章的主要内容包括:

· 什么是 ActiveX 控件以及它们是如何工作的。

· 如何在项目工作区中添加 ActiveX 控件。

· 如何在 Visual C++ 应用程序中使用 ActiveX 控件。

· 如何调用与 ActiveX 控件相关联的各种方法。

· 如何处理由 ActiveX 控件激活的事件。

· 如何用 Visual C++ AppWizard 建立 ActiveX 控件项目。

· 如何用 ClassWizard 向 ActiveX 控件添加属性和方法。

· 如何用 Visual C++ 提供的工具测试自己的 ActiveX 控件。

Page 4: 第十二章  ActiveX  控件的使用和创建

12.1 什么是 ActiveX 控件 在介绍 ActiveX 控件之前有必要了解另外两个编程技术概念:

·OLE ( Object Linked and Embeded ) 对象连接嵌入是 Microsoft

于对象的技术。该技术用于跨越进程和机器边界的数据信息

和操作方法的共享。不过最初的 OLE 仅仅允许把不同的应用

程序创建的文档组合成一个单一文档。

·COM ( Component Object Model ) 组件对象模型是遵循 OLE 基本

技术的对象模型。一个 COM 对象是一个对象定义的实例,该

对象定义指定了该对象的数据和一个或多个作用于该对象的

接口执行方法。客户程序与 COM 对象之间的相互作用只能通

过 COM 对象的接口实现。

Page 5: 第十二章  ActiveX  控件的使用和创建

ActiveX 控件就是一组封装在 COM 对象中的功能模块。这个

COM 对象是独立的,但并不能单独运行,而只能在 ActiveX 容器

中运行,如 Visual C++ 或 Visual Basic 应用程序,这一点很像在组

合设备中插入具有特定功能的组件,例如在组合式音响中,插

入一个 DVD 播放组件。

Page 6: 第十二章  ActiveX  控件的使用和创建

12.1.1 ActiveX 和 IDispatch 接口 每个 COM 对象都有一些标准接口,例如, IUnknown 接口,该接口用来询问是否找到了该组件所支持的其他接口。 每个接口支持一组特定的功能,例如,可以用一个接口来处理控件的可视外观,一个接口来控制控件外观如何与插入该控件的应用程序进行交互,一个接口来触发插入该控件应用程序中的事件,等等。 ActiveX 技术是建立在微软的 COM 技术之上,并使用 COM

的接口和交互模型使 ActiveX 控件与插入控件的应用程序进行完全无缝的集成。 COM 技术奠定了构建 ActiveX 对象的方式及设计ActiveX 接口的方法。 ActiveX 技术定义了建立于 COM 之上的层面、各种对象应该支持什么样的接口以及如何与不同类型的对象交互。

Page 7: 第十二章  ActiveX  控件的使用和创建

ActiveX 控件的关键技术之一是自动。所谓 “自动” 可描述为:

·将一个应用程序中嵌入另一个应用程序。

·当用户的操作涉及到被嵌入者的功能时,激活被嵌入者,并

控制被嵌入者的用户接口或文档部分,同时进行被嵌入者自

身的更改。

·当用户将操作转移到应用程序中非嵌入程序的控制部分时,

被嵌入者自行关闭(例如在 word 应用程序中自动嵌入 Exc

el

电子表格应用程序)。

实现自动工作的关键之一是特殊(调度)接口 IDispatch 。

Page 8: 第十二章  ActiveX  控件的使用和创建

ActiveX 控件可以提供的所有方法有各自的唯一标识值 DISPID 。

这些标识值被存放在用来查找特定方法的标识列表中。 IDispatch

接口由一个指示方法的标识列表和 IDispatch 接口提供的方法组

成。当获取一个特定方法的 DISPID 之后,就可以将该方法的

DISPID 作为参数,通过调用 IDispatch 接口的方法 Invoke 来实现

调用 DISPID 所标识的指定方法。下图示意性描述了 IDispatch 接

口如何使用 Invoke 方法来运行 ActiveX 控件提供的方法,实现的

ActiveX 控件的自动化。

Page 9: 第十二章  ActiveX  控件的使用和创建

DISPID1

DISPID2

DISPID3

DISPID4

DISPIDn

ActiveX 对象

IDispatchvtable

客户程序

IDispatch::Invoke(DISPID)

Invoke(){ switch (DISPID3) { case 1: MethodX(); case 2: MethodY(); case 3: MethodZ(); … }

调度接口

Page 10: 第十二章  ActiveX  控件的使用和创建

12.1.2 ActiveX 容器和服务器 任何可以嵌入另一对象的 ActiveX 对象都是 ActiveX 服务器,

而无论它是一个完整的应用程序或仅仅是一个 ActiveX 控件。

任何可以包含其他被嵌入 ActiveX 服务器的 ActiveX 对象都是

ActiveX 容器。

注意,不要把术语容器和服务器与上图中的客户程序混淆。客

户程序是指调用其嵌入对象的 IDispatch 接口的对象。容器和服

务器都相互调用对方的 IDispatch 接口,因此它们相互成为对方

的客户程序。

Page 11: 第十二章  ActiveX  控件的使用和创建

这两种类型的 ActiveX 对象并不互相排斥。 ActiveX 服务器同时

也可以是 ActiveX 容器,例如,微软的 Internet Explorer Web 浏览

器中 Internet Explorer 是一个可以在 ActiveX 容器外壳中运行的

ActiveX 服务器。可以运行该服务器的 ActiveX 容器外壳还可以包

含 Word 、 Excel 、 Powerpoint 等其他应用程序,同时这些应用程序

还可以作为其他应用程序的 ActiveX 服务器。

ActiveX 控件是 ActiveX 服务器的一个特例,即该 服务器不能

自身运行,必须被嵌入到 ActiveX 容器中。如果在 AppWizard 所

创建的 MFC 应用程序项目中,设置了使用 ActiveX 组件选项,则

该项目所创建的应用程序就自动成为一个 ActiveX 容器。

Page 12: 第十二章  ActiveX  控件的使用和创建

ActiveX 容器和 ActiveX 控件之间的大多数交互操作是通过三个IDispatch 接口完成(如下图所示)。这些 IDispatch 接口中的一个位于控件中,通过该接口,容器可以调用控件的各种方法,为容器的功能提供服务。 容器也为控件提供两个 IDispatch 接口。其中一个接口用于控件在容器中触发事件。另一个接口用于容器为控件设置属性,也就是说 ActiveX 控件的大部分属性实际上由是容器提供,而由控件实现的。当设置属性时,容器调用控件中一个方法,以便通知控件从容器中读取所提供的属性。 Visual C++ 创建了一系列关于 ActiveX 控件接口的 C++ 类,用户只与这些 C++ 类“暴露” 给用户的方法交互,而不需要直接调用控件的 IDispatch 接口,所以上述活动中的大部分对用户来说是“透明”的。

Page 13: 第十二章  ActiveX  控件的使用和创建

ActiveX 容器 ActiveX 控件

IDispatch( 事件 )

IDispatch( 属性 )

IDispatch

Page 14: 第十二章  ActiveX  控件的使用和创建

12.2 在应用程序项目中添加和使用 ActiveX 控件 使用 Visual C++ 使得在应用程序项目添加和使用 ActiveX

控件

变得十分方便。下面通过实例详细讲述如何创建一个可以包含

ActiveX 控件的应用程序项目;如何为这个项目添加 ActiveX 控件

和如何在应用程序中使用所添加的 ActiveX 控件。

Page 15: 第十二章  ActiveX  控件的使用和创建

12.2.1 创建一个可以包含 ActiveX 控件的应用程序1 创建一个 MFC 应用程序 项目,命名为 “ ActiveX” 。

2 选择项目类型为 Dialog Based ,并在创建过程中注意选择项目

具有 ActiveX Controls 支持状态,其他均可取默认选择。

3 删除缺省对话框模板 IDD_ACTIVEX_DIALOG 中的所有缺省控

件,添加一个命令按钮:标识为 IDC_EXIT ,标题为 E&XIT 。4 在缺省创建的 CActiveXDlg 类中,为新添加命令按钮 IDC_EXI

T

的 BN_CLICKED 通知消息建立消息映射 ON_BN_CLICKED(IDC_EXIT, OnExit)

和定义消息处理函数 OnExit 的原型和定义 afx_msg void OnExit();

void CActiveXDlg::OnExit() { OnOK(); }

Page 16: 第十二章  ActiveX  控件的使用和创建

12.2.2 注册 ActiveX 控件 在给应用程序项目添加 ActiveX 控件之前,必须在系统中注册控件。

在系统中注册 ActiveX 控件的方法有两种。一种方法是运行

ActiveX 控件的安装程序,进行自动注册。另一种方法是手工注

册 ActiveX 控件。手工注册的步骤如下:

1 进入 DOS 控制台界面。

2 将当前目录改变到 ActiveX 控件文件所在的目录中,例如:

Windows\system 。

Page 17: 第十二章  ActiveX  控件的使用和创建

3 执行系统命令 regsvr32 ,并指定 ActiveX 控件名为该命令的参

数。例如要注册一个文件名为 MYCTL.OCX 的 ActiveX 控件,

假如该控件文件 MYCTL.OCX 在 Windows\system 目录中,则可

执行如下命令:

C:\WINDOWS> cd system

C:\WINDOWS\SYSTEM> regsvr32 myctl.ocx

Page 18: 第十二章  ActiveX  控件的使用和创建

注意:

·手工注册可能会导致所注册的控件缺少某些信息,从而在开

发中无法使用,所以建议使用控件所带的安装程序。

· 如果所使用的 ActiveX 控件在系统安装时已经被缺省注册了,

则不需要使用上述方法进行控件的注册。本例中要添加的控

件就是这类 ActiveX 控件。

ActiveX 控件一旦在系统中注册成功,就可以将它添加到应用

程序项目中。

在 Visual C++ 6.0 中注册和添加 ActiveX 控件的步骤如下:

Page 19: 第十二章  ActiveX  控件的使用和创建

1 选择 Project->Add To Project->Components and Controls 。

2 在弹出的“ Components and Controls Gallery” 对话框中,选择 “Registed ActiveX Controls” 文件夹:

Page 20: 第十二章  ActiveX  控件的使用和创建

3 在该文件夹中,查找并选中要添加的已注册 ActiveX 控件,本

例中选择 Microsoft FlexGrid Control version 6.0 控件,双击被

选中控件选项,或按《 Insert》按钮。

Page 21: 第十二章  ActiveX  控件的使用和创建

4 在提示是否确实要添加该控件的对话框中,按《 OK》按钮。

5 在 “ Confirm Classes” 对话框中,单击《 OK》按钮添加控件所 包含的全部或部分 C++ 类:

Page 22: 第十二章  ActiveX  控件的使用和创建

6 在 “ Components and Control Gallery” 对话框中单击《 Close》按

钮完成为项目添加控件的工作。

7 控件 FlexGird 已经被添加到资源编辑器的 “ Control Palette”

上:

Page 23: 第十二章  ActiveX  控件的使用和创建

8 查看工作区的 Class View ,发现项目中已自动增加了与 Flex

Gird

控件相关的类:

CMSFlexGrid 、 COleFont 、 CRowCursor 和 CPicture ,每个类中都

提供了相应的方法。

Page 24: 第十二章  ActiveX  控件的使用和创建

在 Visual C++ .NET 中注册和添加 ActiveX 控件的步骤如下:

1 在 Toolbox 中,单击鼠标右键弹出的环境菜单中选择菜单项 “ Choose Items…” :

Page 25: 第十二章  ActiveX  控件的使用和创建

2 在弹出的属性表《 Choose Toolbox Items》中,选择属性页 “ COM

Components” ,在该属性页中选择所需的 ActiveX 控件 “ Fle

xGird”

后,按 OK 按钮。

Page 26: 第十二章  ActiveX  控件的使用和创建

3 添加了 ActiveX 控件 “ FlexGird” 后的 Toolbox 如下:

注意,经过上述操作后,并不会在项目中增加封装 “ FlexGird” 控件的类 CMsfgrid (相应的定义文件和实现文件)。只有将控件从 ToolBox 中添加到对话框模板中,控件的类 CMsfgrid (相应的定义文件和实现文件)才会被自动添加到项目中。

Page 27: 第十二章  ActiveX  控件的使用和创建

12.2.3 在对话框模板中添加 ActiveX 控件 ActiveX 控件添加到项目中之后,便可以像使用其他标准控件

一样,把它添加对话框模板中。本例中所添加的 ActiveX 控件

FlexGird 的主要属性设置如下:

控件名 属性 设置值

FlexGird ID IDC_MSFGRID

Rows 20

Cols 4

MergeCells Restrict All

Format String <Region |<Product |<Employee |<Sales

Page 28: 第十二章  ActiveX  控件的使用和创建

在完成对控件所有属性的设置之后,需要为该控件添加一个数

值类对象 m_ctrlFGrid ,以便能和代码中的控件进行交互。所添

加的代码如下:class CActiveXDlg : public CDialog

{ …

public:

CActiveXDlg(CWnd* pParent = NULL); // standard constructor

enum { IDD = IDD_ACTIVEX_DIALOG };

CMSFlexGrid m_ctrlFGrid;

};

Page 29: 第十二章  ActiveX  控件的使用和创建

12.2.4 在应用程序中使用 ActiveX 控件

12.2.4.1 与 ActiveX 控件进行交互 本例中将使用添加的 FlexGrid 控件生成一个产品销售数字统

计表,其中包括 4 个销售人员在 5 个销售区的销售情况。要求能

够在屏幕上滚动显示数据,这些数据应按能区域或产品种类分

类,以比较各个销售人员在每种产品上的销售业绩。为此,首

先调用 FlexGrid 控件的方法 SetTextArray 将要处理、显示的数据

存入到控件的数组中,并将数组中数据将被载入表格的相应单

元格中。调用 FlexGrid 控件的内置排序方法 SetSort ,使表格按

升序排列。为了实现这些操作需要为 CActiveXDlg 类添加如下成员函数定义:

Page 30: 第十二章  ActiveX  控件的使用和创建

1 把数据载入控件

添加一个私有成员函数将数据装载到 FlexGrid 控件中,该函

数命名为 LoadData ,函数类型为 void ,其定义代码如下:void CActiveXDlg::LoadData()

{

int liCount; // The grid row count

CString lsAmount; // The sales amount

// Initialize the random number generator

srand((unsigned)time(NULL));

// Create Array in the control

for( liCount = m_ctrlFGrid.GetFixedRows();

liCount < m_ctrlFGrid.GetRows(); liCount++ )

Page 31: 第十二章  ActiveX  控件的使用和创建

{

// Generate the first column (region) values

m_ctrlFGrid.SetTextArray( GenID(liCount, 0), RandomStringValue(0));

// Generata the second column (product) values

m_ctrlFGrid.SetTextArray( GenID(liCount, 1), RandomStringValue(1));

// Generate the third column (employee) values

m_ctrlFGrid.SetTextArray( GenID(liCount, 2), RandomStringValue(2));

// Generata the sales amount values

lsAmount.Format( "%5d.00", rand());

// Populate the fourth column

m_ctrlFGrid.SetTextArray(GenID(liCount, 3), lsAmount);

}

Page 32: 第十二章  ActiveX  控件的使用和创建

// Merge the Common subsequent rows in these columns

m_ctrlFGrid.SetMergeCol(0, TRUE);

m_ctrlFGrid.SetMergeCol(1, TRUE);

m_ctrlFGrid.SetMergeCol(2, TRUE);

DoSort(); // Sort the grid

}

Page 33: 第十二章  ActiveX  控件的使用和创建

代码分析:·函数循环处理控件中所有行,给每个单元格中放入数据。通 过调用控件的 GetRows 方法可获得控件中总行数,而调用控 件的 GetFixedRows 方法可获得有标题行的编号。通过调用控 件的 SetTextArray 方法可把数据添加到控件单元格中,调用 该方法的两个参数是由函数 GetID 获取单元格的 ID 和使用函 数 RandomStringValue 产生要放入控件单元格的数据。这两个 函数都是 CActiveXDlg 类的新增成员函数。· 把数据放入表格单元格后,调用控件的方法 SetMergeCol ,用 于通知控件,如果相邻行有着同样的值,可以把前三列的单 元格合并。· 最后,使用另一个 CActiveXDlg 类的新增函数来完成单元格数 据的排序。

Page 34: 第十二章  ActiveX  控件的使用和创建

2 计算单元格 ID

控件 FlexGrid 的单元格按从左至右、从上至下编号。计算单元格 ID 的函数 GetID 的访问权限为 private ,其定义代码如下:int CActiveXDlg::GenID(int m_iRow, int m_iCol)

{

int liCols = m_ctrlFGrid.GetCols(); // Get the number of column

return (m_iCol + liCols * m_iRow); // Generate an ID

}

3 生成随机数据 实现这一功能的函数 RandomStringValue 将根据参数 —— 单元格的当前列号分别为第一列产生随机的销售区域名,为第二列产生随机的销售产品名,为第三列产生随机的销售人员名。该函数的访问权限也为 private ,其定义代码如下:

Page 35: 第十二章  ActiveX  控件的使用和创建

CString CActiveXDlg::RandomStringValue (int m_iColumn)

{

CString lsStr; // The return string

int liCase; // A random value ID

// Which column are we generating for

switch(m_iColumn)

{

case 0: // The first column (region)

liCase = (rand() % 5); // Generate a random value between 0 and 4

// What value was generated?

switch(liCase)

{

case 0: // 0 - Northwest region

lsStr = "Northwest"; break;

Page 36: 第十二章  ActiveX  控件的使用和创建

case 1: // 1 - Southwest region

lsStr = "Southwest"; break;

case 2: // 2 - Midwest region

lsStr = "Midwest"; break;

case 3: // 3 - Northeast region

lsStr = "Northeast"; break;

case 4: // 4 - Southeast region

lsStr = "Southeast"; break;

}

break;

case 1: // The second column (product)

liCase = (rand() % 5); // Generate a random value between 0 and 4

// What value was generated?

Page 37: 第十二章  ActiveX  控件的使用和创建

switch(liCase)

{

case 0: // 0 - Dodads

lsStr = "Dodads"; break;

case 1: // 1 - Thingamajigs

lsStr = "Thingamajigs"; break;

case 2: // 2 - Whatchamacallits

lsStr = "Whatchamacallits"; break;

case 3: // 3 - Round Tuits

lsStr = "Round Tuits"; break;

default: // 4 - Widgets

lsStr = "Widgets";

}

break;

Page 38: 第十二章  ActiveX  控件的使用和创建

case 2: // The third column (employee)

liCase = (rand() % 4); // generate a random value between 0 and 3

// What value was generated?

switch(liCase)

{

case 0: // 0 - Dore

lsStr = "Dore"; break;

case 1: // 1 - Harvey

lsStr = "Harvey"; break;

case 2: // 2 - Pogo

lsStr = "Pogo"; break;

default: // 3 - Nyra

lsStr = "Nyra";

}

Page 39: 第十二章  ActiveX  控件的使用和创建

break;

}

// Return the generated string

return lsStr;

}

Page 40: 第十二章  ActiveX  控件的使用和创建

4 为控件中显示数据排序

为 CActivexDlg 类定义私有成员函数 DoSort 用以调用 FlexGrid

控件的排序函数 SetSort 实现对控件的排序。排序的方式是升序

还是降序取决于调用 SetSort 的参数。其定义代码如下:void CActiveXDlg::DoSort()

{ // Set the current column to column 0

m_ctrlFGrid.SetCol(0);

// Set the column selection to all columns

m_ctrlFGrid.SetColSel((m_ctrlFGrid.GetCols() - 1));

// Generic Ascending sort

m_ctrlFGrid.SetSort(1);

}

Page 41: 第十二章  ActiveX  控件的使用和创建

5 修改 CActiveXDlg::OnInitDialog

在此函数中加入对函数 LoadData 的调用,实现对 FlexGrid

件的初始化。BOOL CActiveXDlg::OnInitDialog()

{

CDialog::OnInitDialog();

LoadData(); // Load data into the Grid control

return TRUE; // return TRUE unless you set the focus to a control

}

6 编译运行 “ ActiveX”

Page 42: 第十二章  ActiveX  控件的使用和创建

12.2.4.2 响应控件事件 运行上面的程序,你会发现 FlexGrid 控件对任何输入事件都

没有响应。这是因为虽然 ActiveX 控件为 Visual C++ 应用程序提

供了多种事件,但大多数 ActiveX 控件并没有与可用事件相关联

的缺省功能,而必须告诉控件在每个事件发生时做些什么。

在本例中,我们将添加两个控件事件的响应,使用户可以按

住鼠标左或右键选中列标题并将它拖动到另一个位置,从而重

新安排列的顺序。实现此功能,必须捕获两个控件事件:鼠标

左或右键被按下和被释放。

· 在鼠标按钮按下事件中:需要检查用户单击了列标题,如果

是,应捕获所选中的列。

Page 43: 第十二章  ActiveX  控件的使用和创建

· 在鼠标按钮释放事件中:需要将所捕获的列移动到鼠标被释

放时所处的列位置。

要完成这项功能,还需要在 CActiveXDlg 类中增加一个私有数

据成员,用于保存所捕获列的列号,所添加的代码如下:

private:

int m_iMouseCol;

1 捕获所选列

· 使用 ClassWizard 为 IDC_MSFGRID 控件对象的 MouseDown

事件

消息添加响应函数;

· 编写函数代码如下:

Page 44: 第十二章  ActiveX  控件的使用和创建

void CActiveXDlg::OnMouseDownMsfgrid( short Button, short Shift,

long x, long y )

{ // TODO: Add your control notification handler code here

// Did the user click on a data row and not the header row?

if(m_ctrlFGrid.GetMouseRow() != 0)

{

// If so, then zero out the column variable and exit

m_iMouseCol = 0;

return;

}

// Save the column Clicked on

m_iMouseCol = m_ctrlFGrid.GetMouseCol();

}

Page 45: 第十二章  ActiveX  控件的使用和创建

2 把列移动到鼠标被释放处

· 使用 ClassWizard 为 IDC_MSFGRID 控件对象的 MouseUp 事件消

息添加响应函数;

· 编写函数代码如下:void CActiveXDlg::OnMouseUpMsfgrid (short Button, short Shift,

long x, long y)

{ // TODO: Add your control notification handler code here

// If the selected column was the first column, there's nothing to do

if (m_iMouseCol == 0) return;

m_ctrlFGrid.SetRedraw(FALSE); // Turn the control redraw off

m_ctrlFGrid.SetColPosition(m_iMouseCol,

m_ctrlFGrid.GetMouseCol());

// Change the selected column position

Page 46: 第十二章  ActiveX  控件的使用和创建

DoSort(); // Resort the grid

m_ctrlFGrid.SetRedraw(TRUE); // Trun redraw back on

}

这两个事件消息的映射如下:BEGIN_EVENTSINK_MAP(CActiveXDlg, CDialog)

//{{AFX_EVENTSINK_MAP(CActiveXDlg)

ON_EVENT(CActiveXDlg, IDC_MSFGRID, -605 /* MouseDown */,

OnMouseDownMsfgrid, VTS_I2 VTS_I2 VTS_I4 VTS_I4)

ON_EVENT(CActiveXDlg, IDC_MSFGRID, -607 /* MouseUp */,

OnMouseUpMsfgrid, VTS_I2 VTS_I2 VTS_I4 VTS_I4)

//}}AFX_EVENTSINK_MAP

END_EVENTSINK_MAP()

3 编译运行 “ ActiveX”

Page 47: 第十二章  ActiveX  控件的使用和创建

12.3 创建 ActiveX 控件 本例通过制作一个能绘制涂鸦的控件实例,描述一个 Activ

eX

控件的一般创建方法,并通过测试工具和一个应用实例来验证

该控件的设计功能。

· 控件功能:能在一个可确定的矩形区域中通过点击鼠标或通

过其他方法获得模拟的鼠标点击事件,以便随机绘制涂鸦,

并且能将所绘制的涂鸦保存到一个磁盘文件中和能使用该磁

盘文件所保存的数据恢复涂鸦。为实现这些功能,所设计的

控件应具有以下属性、方法和事件:

Page 48: 第十二章  ActiveX  控件的使用和创建

· 控件属性:属性是指控件中可见的、容器应用程序可修改的

属性数据。四种基本的属性类型是:

环境 (ambient) 由容器应用程序提供给控件,例如,背景

颜色或缺省字体,使控件看上去就像是容器应用程序的

一部分;

扩展 (extended) 由容器应用程序提供并实现的属性,控件

可以对这些属性做一定扩展;

库存 (stock) 由 ActiveX 控件开发工具实现,例如控件的

字体或控件的背景颜色;

定制 (custom) 是最需要关注的属性,因为这些属性是所

设计的控件专有的,并且直接与控件的功能有关。

本例需要三个 custom 属性:

Page 49: 第十二章  ActiveX  控件的使用和创建

NumberSquiggles 绘制涂鸦的最多段数;SquiggleLength 一段涂鸦的最大长度;KeepCurrentDrawing 是否保持当前所绘制的涂鸦。

为了能在容器应用程序中访问和修改这些属性还需要为每个 属性提供相应的 Get 和 Set 方法,即把这些属性“暴露”给容器 应用程序。· 控件方法:是控件中的能被容器应用程序调用的函数。在本 控件中除了要提供三个属性的访问和设置方法,以及显示控 件版本信息的方法外,还需要提供下列三个与控件功能密切 相关的方法: DoClick 用于模拟在控件区域内鼠标点击操作; SaveDrawing 用于保存当前的涂鸦为一个磁盘文件; LoadDrawing 使用磁盘文件中保存的数据恢复涂鸦。

Page 50: 第十二章  ActiveX  控件的使用和创建

· 控件事件:事件是控件发给容器应用程序的通知消息。它们 用于通知容器应用程序某种事件已经发生,以便容器应用程 序在需要时采取相应的措施。从控件中可以触发两类事件: 库存事件 通过 ActiveX 控件开发工具实现,可以在控件内以

函数调用的方式使用,也可以使你触发容器应用程序中的鼠标或键盘事件、错误或状态的变化。

定制事件 这些事件与控件的特定功能相关联。这类事件可

以指定与事件一起传递给容器的参数,使容器能得到所需要的数据,以便对事件消息作出反应。

本控件中定义了三个事件: click 库存事件,通知在控件区域中发生了鼠标点击; FileStored 定制事件,通知当前保存涂鸦已经成功或失败; FileLoaded 定制事件,通知当前恢复涂鸦已经成功或失败。

Page 51: 第十二章  ActiveX  控件的使用和创建

12.3.1 创建控件外壳 使用 MFC ActiveX Control Wizard (控件创建向导)为要创建的

任何 ActiveX 控件建立一个 “外壳”(“ Squiggle” ActiveX 控件项目)。

它将生成所有必要的文件,并配置项目,这样在编译项目时,编译器就会建立一个 ActiveX 控件。

在 Visual C++ 6.0 中,该项目创建的方法和操作步骤如下:

Page 52: 第十二章  ActiveX  控件的使用和创建
Page 53: 第十二章  ActiveX  控件的使用和创建

创建此项目需要经过下列两步:

1 在 “ Step 1 of 2” 对话框中进行如下操作:

Page 54: 第十二章  ActiveX  控件的使用和创建

·确定项目中包含几个 ActiveX 控件,默认的控件数为 1 。

·确定所创建的控件是否需要运行时许可证,这是一种防止控

件的使用者在没有购买运行时许可证的情况下使用控件进行

开发的措施。默认选择为不需要运行时许可证。

·确定是否为程序源代码生成注释,默认选择为生成注解。

·确定是否为控件生成帮助文件,默认选择为没有帮助文件。

本例中均使用默认选择。

Page 55: 第十二章  ActiveX  控件的使用和创建

2 在 “ Step 2 of 2” 对话框中继续进行下列操作:

· 如果需要,可以选择浏览或修改控件的类名和文件名。组合

列表框中包含了项目中所有要创建的 ActiveX 控件类名。

Page 56: 第十二章  ActiveX  控件的使用和创建

·选择所创建的控件具有的特性:

① 控件在可见时被激活(默认特性);

② 运行时不可见;

③ 在 “ Insert Object” 对话框中可使用;

④ 具有版本对话框 “ About” (默认特性);

⑤ 作用为一个简单的框架控件。

·选择一种窗口类(在组合列表框中列出的)作为所创建控件

的子类,例如,欲创建一个特殊的编辑框,以便对用户输入

该编辑框的任何内容进行一些编辑,就可以选择 EDIT 类作为

控件的子类。默认选择为不选择任何窗口类,即 none 。

Page 57: 第十二章  ActiveX  控件的使用和创建

·按 Advanced 按钮可以进行高级设置,但要求对 ActiveX 控件有

相当深入的了解,所以一般可以不做此项选择。

本例中均使用默认选择。

3 按《 Finish》按钮完成 ActiveX 控件外壳的创建。

Page 58: 第十二章  ActiveX  控件的使用和创建

在 Visual C++ .NET 中,该项目创建的方法和操作步骤如下:

1 创建一个名为 “ Squiggle” ActiveX 控件项目:

Page 59: 第十二章  ActiveX  控件的使用和创建

2 保持该 ActiveX 控件项目不具有运行许可证的缺省设置:

Page 60: 第十二章  ActiveX  控件的使用和创建

3 保持该 ActiveX 控件项目的所有命名缺省设置:

Page 61: 第十二章  ActiveX  控件的使用和创建

4 保持该 ActiveX 控件项目的所有控件属性缺省设置:

5 按《 Finish》按钮完成 ActiveX 控件外壳的创建。

Page 62: 第十二章  ActiveX  控件的使用和创建

12.3.2 增加进行绘制涂鸦的类 CLine 和 CModArt

1 CLine 类

分解涂鸦的绘制操作是由一系列的随机线段(线段的位置、

长度、颜色和线宽均随机发生)组成的。因此,创建一个类

CLine ,将描述这些线段的属性和操作封装在其中,并在整个涂

鸦的绘制过程中,逐个随机创建 CLine 类对象。

⑴ 定义 CLine 类

使用 ClassWizard 定义一个 Generic Class 类 CLine ,并在 Base

Class 列表框的第一行输入 CObject 作为基类,保留其 publi

c

属性。

Page 63: 第十二章  ActiveX  控件的使用和创建

注意:

① 由于编译器找不到 CObject 类的定义,因此,会在确认定

义 CLine 类时,显示如下信息框:

Page 64: 第十二章  ActiveX  控件的使用和创建

你可以不必理会它,而按 “确认” 按钮完成 CLine

的创建。

但如果你使用的基类是比 MFC 类层次更低的自定义类,则

需要留意此信息,并将必须的头文件包含( #includ

e )到源

代码文件中。

② 在 Visual C++ .NET 中,定义 CLine 时不会发生上述问题。

Page 65: 第十二章  ActiveX  控件的使用和创建

② 为了使 CLine 类具有持续性,需要在 CLine 类的定义文件

中加入宏:class CLine : public CObject

{

DECLARE_SERIAL(CLine)

};

在实现文件中加入如下宏:…

IMPLEMENT_SERIAL(CLine, CObject, 1)

Page 66: 第十二章  ActiveX  控件的使用和创建

⑵ 为 CLine类增加描述线段的属性class CLine : public CObject

{

private:

COLORREF m_crColor; // 线段颜色

CPoint m_ptFrom; // 线段起点

CPoint m_ptTo; // 线段终点

UINT m_pnWidth; // 画笔宽度

};

Page 67: 第十二章  ActiveX  控件的使用和创建

⑶ 增加一个能为线段属性赋值的构造函数CLine::CLine (CPoint ptFrom, CPoint ptTo,

UINT pnWidth, COLORREF crColor)

{

// Initialize the from and to points

m_ptFrom = ptFrom;

m_ptTo = ptTo;

m_pnWidth = pnWidth;

m_crColor = crColor;

}

Page 68: 第十二章  ActiveX  控件的使用和创建

⑷ 定义绘制线段的成员函数 Draw 并为其编码void CLine::Draw(CDC *pDC)

{

// Create a pen

CPen lpen(PS_SOLID, m_pnWidth, m_crColor);

// Set the new pen as the drawing object

CPen* pOldPen = pDC->SelectObject(&lpen);

// Draw the line

pDC->MoveTo(m_ptFrom);

pDC->LineTo(m_ptTo);

// Reset the previous pen

pDC->SelectObject(pOldPen);

}

Page 69: 第十二章  ActiveX  控件的使用和创建

⑸ 手工重载 CObject 的持续化虚成员函数 Serialize 并为其编码void CLine::Serialize(CArchive &ar)

{

CObject::Serialize(ar);

if(ar.IsStoring())

ar<<m_ptFrom<<m_ptTo<<(DWORD)m_crColor<<m_pnWidth;

else

ar>>m_ptFrom>>m_ptTo>>(DWORD)m_crColor>>m_pnWidth;

}

Page 70: 第十二章  ActiveX  控件的使用和创建

2 CModArt 类

创建此类的目的就是要实现对一个完整涂鸦画面的描述和围

绕绘制涂鸦所需要的所有操作。

⑴ 定义 CModArt 类

与定义 CLine 类相似,使用 ClassWizard 创建一个 Generi

c Class

类 CModArt ,并在 Base Class列表框的第一行输入 CObject 作为基

类,保留其 public 属性。虽然 CModArt 类也需要实现持续性,

但不需要在类定义文件和实现文件中加入实现持续性的宏,原

因是 CModArt 的持续性可以通过 CLine 的持续性实现。

Page 71: 第十二章  ActiveX  控件的使用和创建

⑵ 为 CModArt 类增加描述整幅涂鸦的属性class CModArt : public CObject

{

public:

CRect m_rDrawArea; // 绘制涂鸦的区域

CObArray m_oaLines; // 用于存放组成涂鸦的所有线段的数组

private:

int m_iLength; // 组成一条涂鸦线的最多线段数

int m_iSegments; // 组成整幅涂鸦画面的最多涂鸦线数

};

Page 72: 第十二章  ActiveX  控件的使用和创建

⑶ 为 CModArt 添加一些访问和修改属性的公有成员函数int CModArt::GetLength()

{

// Return the current value for the m_iLength variable

return m_iLength;

}

void CModArt::SetLength(int iLength)

{

// Set the current value for the m_iLength variable

m_iLength = iLength;

}

Page 73: 第十二章  ActiveX  控件的使用和创建

int CModArt::GetSegments()

{

// Return the current value for the m_iSegments variable

return m_iSegments;

}

void CModArt::SetSegments(int iSegments)

{

// Set the current value for the m_iSegments variable

m_iSegments = iSegments;

}

void CModArt::SetRect(CRect rDrawArea)

{

// Set the drawing area rectangle

m_rDrawArea = rDrawArea;

}

Page 74: 第十二章  ActiveX  控件的使用和创建

⑷ 为 CModArt 添加生成一条涂鸦线的私有成员函数void CModArt::NewLine()

{

int lNumLines, lCurLine;

UINT nCurWidth;

CPoint pTo, pFrom;

int cRed, cBlue, cGreen;

// Normalize the rectangle before determining the width and height

m_rDrawArea.NormalizeRect();

// Get the area width and height

int lWidth = m_rDrawArea.Width();

int lHeight = m_rDrawArea.Height();

// Determine the number of parts to this squiggle

lNumLines = rand() % m_iLength;

// Are there any parts to this squiggle?

Page 75: 第十二章  ActiveX  控件的使用和创建

if(lNumLines > 0)

{ // Determine the color

cRed=rand() % 256; cBlue=rand() % 256; cGreen=rand() % 256;

// Determine the pen width

nCurWidth = (rand() % 8) + 1;

// Determine the strat point for the squiggle

pFrom.x = (rand() % lWidth) + m_rDrawArea.left;

pFrom.y = (rand() % lHeight) + m_rDrawArea.top;

// Loop through the number of segments

for(lCurLine = 0; lCurLine < lNumLines; lCurLine++)

{ // Determine the end point of the segment

pTo.x = ((rand() % 23) - 10) + pFrom.x;

pTo.y = ((rand() % 23) - 10) + pFrom.y;

// Create a new Cline object

CLine *pLine = new CLine(pFrom, pTo, nCurWidth,

RGB(cRed,cBlue,cGreen));

Page 76: 第十二章  ActiveX  控件的使用和创建

try // Add the new line to the object array

m_oaLines.Add(pLine);

// Did we run into a memory exception

catch(CMemoryException* perr)

{ // Display a messge for the user, giving him the bad news

AfxMessageBox("Out of memory", MB_ICONSTOP|MB_OK);

// Did we create a line object?

if(pLine)

delete pLine; pLine = NULL; // Delete it

perr->Delete(); // Delete the exception object

}

pFrom = pTo; // Set the starting point to the end point

}

}

}

Page 77: 第十二章  ActiveX  控件的使用和创建

⑸ 为 CModArt 添加生成整幅涂鸦的公有成员函数void CModArt::NewDrawing()

{

int lNumLines, lCurLine;

// Determine how many lines to create

lNumLines = rand() % m_iSegments;

// Are there any lines to create?

if(lNumLines > 0)

{ // Loop through the number of lines

for(lCurLine = 0; lCurLine < lNumLines; lCurLine++)

NewLine(); // Create the new line

}

}

Page 78: 第十二章  ActiveX  控件的使用和创建

⑹ 修改 CModArt 的构造函数CModArt::CModArt()

{

// Initialize the random number generator

srand((unsigned)time(NULL));

// Initialize the propty variables

m_iLength = 200;

m_iSegments = 50;

}

Page 79: 第十二章  ActiveX  控件的使用和创建

⑺ 为 CModArt 添加绘制整幅涂鸦的公有成员函数 Draw

void CModArt::Draw(CDC *pDC)

{ // Get the number of lines in the object array

int liCount = m_oaLines.GetSize();

int liPos;

// Are ther any objects in the array?

if(liCount)

{ // Loop through the array, drawing each object

for(liPos = 0; liPos < liCount; liPos++)

((CLine*)m_oaLines[liPos])->Draw(pDC);

}

}

Page 80: 第十二章  ActiveX  控件的使用和创建

⑻ 为 CModArt 添加清除整幅涂鸦的公有成员函数 ClearDrawin

g

void CModArt::ClearDrawing()

{ // Get the number of lines in the object array

int liCount = m_oaLines.GetSize();

int liPos;

if(liCount) // Are there any objects in the array

{ // Loop through the array, deleting each object

for(liPos = 0; liPos < liCount; liPos++)

delete m_oaLines[liPos];

m_oaLines.RemoveAll(); // Reset the array

}

}

Page 81: 第十二章  ActiveX  控件的使用和创建

⑼ 为 CModArt 重载持续化函数 Serialize

void CModArt::Serialize(CArchive &ar)

{ // Pass the archive object on to the array

m_oaLines.Serialize(ar);

}

显然, CModArt 类的持续化操作是通过数组中保存的 CLin

e 类

对象的持续化函数实现的。

在控件类 CSquiggleCtrl 中添加 CModArt 类对象 m_maDra

wing ,

访问权限为私有。该对象成员用于在控件中调用 CModArt 的方

法实现绘制涂鸦。注意在 CSquiggleCtrl 类的定义文件中包含

CModArt 类的头文件。

Page 82: 第十二章  ActiveX  控件的使用和创建

12.3.3 为控件添加属性 为了在使用控件时能访问和修改 CModArt 的属性 m_iLeng

th 和

m_iSegments ,需要为 CSquiggleCtrl 添加相应的属性。

1 添加属性 SquiggleLength

⑴ 在 DSquiggle 处,单击鼠标右键,弹出环境菜单,并选择菜单项 “ Add Property…” :

Page 83: 第十二章  ActiveX  控件的使用和创建

在 Visual C++ 6.0 中: 在 Visual C++ >NET 中:

Page 84: 第十二章  ActiveX  控件的使用和创建

⑵ 在 “ Add Property” 对话框中,将该属性命名为 SquiggleLen

gth ,

并指定其类型为 short 或 SHORT 类型。接受系统自动为该属

性命名的内部变量名 m_squiggleLength 或 m_SquiggleLength ,以

及为该属性自动增加的通知函数 OnSquiggleLengthChanged 。保

持缺省的属性实现类型( Member variable )。单击《 OK》或

《 Finish》按钮,完成该属性的添加操作。

上述操作如下图所示:

Page 85: 第十二章  ActiveX  控件的使用和创建

在 Visual C++ 6.0 中:

Page 86: 第十二章  ActiveX  控件的使用和创建

在 Visual C++ .NET 中:

Page 87: 第十二章  ActiveX  控件的使用和创建

⑶ 为 OnSquiggleLengthChanged 添加执行代码:在 Visual C++ 6.0 中的代码如下:short CSquiggleCtrl::OnSquiggleLengthChanged()

{

m_maDrawing.SetLength(m_squiggleLength); // 添加的代码 SetModifiedFlag(); // 缺省代码}

在 Visual C++ .NET 中的代码如下:void CSquiggleCtrl::OnSquiggleLengthChanged()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 缺省代码 m_maDrawing.SetLength(m_squiggleLength); // 添加的代码 SetModifiedFlag(); // 缺省代码}

Page 88: 第十二章  ActiveX  控件的使用和创建

其中:

·m_maDrawing 是 CSquiggleCtrl 的 CModArt 类对象成员。

· 调用 CModArt 的成员函数 SetLength 将控件从容器中获得属性

值传递给 CModArt 类对象成员。

· 调用 SetModifiedFlag 设置控件被修改标志。

Page 89: 第十二章  ActiveX  控件的使用和创建

2 添加属性 NumberSquiggles

添加方法与 SquiggleLength 相同,系统为该属性命名的内部变量为 m_numberSquiggles ,通知函数为 OnNumberSquigglesC

hanged

该函数的源代码在 Visual C++ 6.0 中的代码如下:short CSquiggleCtrl::OnNumberSquigglesChanged()

{

m_maDrawing.SetSegments(m_numberSquiggles); // 添加的代码

SetModifiedFlag(); // 缺省代码

}

在 Visual C++ .NET 中的代码如下:void CSquiggleCtrl::OnNumberSquigglesChanged()

{

Page 90: 第十二章  ActiveX  控件的使用和创建

AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 缺省代码

m_maDrawing.SetSegments(m_numberSquiggles); // 添加的代码

SetModifiedFlag(); // 缺省代码

}

3 添加属性 KeepCurrentDrawing

添加方法 SquiggleLength 相同,而该属性的类型为 BOOL

型或 VARIANT_BOOL ,系统为该属性命名的内部变量为

m_KeepCurrentDrawing ,通知函数为 OnKeepCurrentDrawingCh

anged 。但该函数不必添加代码。

Page 91: 第十二章  ActiveX  控件的使用和创建

12.3.4 设计和创建属性页 为了使用户能使用你制作的 ActiveX 控件,控件必须提供一个

属性页。该属性页能向用户提供一种设置该控件属性的手段,

即使用户所使用的开发工具不能通过代码以外的手段来访问这

些属性。

向控件中添加属性页是非常容易的,浏览控件的资源,就会

发现控件的属性页对话框模板已经在控件外壳的创建过程中被

自动构建了。你只要根据需要编辑这个对话框模板和为对话框

中的控件定义必要的关联变量成员就可以完成属性页的设计和

创建。在本例中,设计创建属性页的具体步骤如下:

Page 92: 第十二章  ActiveX  控件的使用和创建

1 设计、编辑属性页对话框模板

在属性对话框模板中添加所需要的标准控件,并对这些控件

进行布局。本例中的属性页对话框模板如下所示:

Page 93: 第十二章  ActiveX  控件的使用和创建

属性页对话框中各控件描述如下:

控件类别 控件标识 控件标题

Static Text IDC_STATIC Maximum Number of Squiggles:

Edit Box IDC_ENBRSQUIG

Static Text IDC_STATIC Maximum Length og Squiggles:

Edit Box IDC_ELENSQUIG

Check Box IDC_CMODARTINTDRAW Maintain Current Drawing

Page 94: 第十二章  ActiveX  控件的使用和创建

2 为控件定义关联的数据成员

注意,所定义的这些数据成员要与所创建的 ActiveX 控件的相

应属性的外部名进行关联,本例中,为控件 IDC_ENBRSQUIG

义的数据成员 m_iNbrSquiggles 必须与所创建的控件 Squiggle

属性 NumberSquiggles 相关联。具体方法如下:

Page 95: 第十二章  ActiveX  控件的使用和创建
Page 96: 第十二章  ActiveX  控件的使用和创建

对话框中的组合列表框 Optional property name 用来确定与所定义的数据成员相关联的控件属性外部名。如果所关联的属性是定制属性,则将该属性的外部名直接键入列表框中;如果所关联的属性是库存属性,则可以从该列表框的下拉表中选取属性外部名。本例中所定义的属性页控件数据成员如下:

通过 ClassWizard 定义属性页的上述数据成员,会在属性页的数据交换函数 DoDataExchange 中自动添加了相应的代码:

变量标识 变量名 类别 类型 关联的属性外部名

IDC_CMODARTINTDRAW m_bKeepDrawing Value BOOL KeepCurrentDrawing

IDC_ELENSQUIG m_iLenSquig Value Int SquiggleLength

IDC_ENBRSQUIG m_iNbrSquiggles Value int NumberSquiggles

Page 97: 第十二章  ActiveX  控件的使用和创建

void CSquigglePropPage::DoDataExchange(CDataExchange* pDX)

{ //{{AFX_DATA_MAP(CSquigglePropPage)

DDX_Text(pDX, IDC_ELENSQUIG, m_iLenSquig);

DDP_Text(pDX,IDC_ELENSQUIG, m_iLenSquig, _T("SquiggleLength"));

DDX_Text(pDX, IDC_ENBRSQUIG, m_iNbrSquiggles);

DDP_Text(pDX, IDC_ENBRSQUIG, m_iNbrSquiggles,

_T("NumberSquiggles") );

DDX_Check(pDX, IDC_CMODARTINTDRAW, m_bKeepDrawing);

DDP_Check(pDX, IDC_CMODARTINTDRAW, m_bKeepDrawing,

_T("KeepCurrentDrawing") );

//}}AFX_DATA_MAP

DDP_PostProcessing(pDX);

}

Page 98: 第十二章  ActiveX  控件的使用和创建

3 为 CSquiggleCtrl 的属性交换函数 DoPropExchange 添加代码

DoPropExchange 的功能与对话框的 DoDataExchange 的功能

类似,它可以实现控件属性与控件属性页之间的数据交换。void CSquiggleCtrl::DoPropExchange(CPropExchange* pPX)

{

ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));

COleControl::DoPropExchange(pPX);

// TODO: Call PX_ functions for each persistent custom property.

PX_Bool(pPX,"KeepCurrentDrawing", m_keepCurrentDrawing, FALSE);

PX_Short(pPX,"NumberSquiggles", m_numberSquiggles, 200);

PX_Short(pPX,"SquiggleLength", m_squiggleLength, 50);

}

Page 99: 第十二章  ActiveX  控件的使用和创建

·KeepCurrentDrawing 、 NumberSquiggles 和 SquiggleLength 是控件

属性页中用于设置各种属性值的操作控件的外部名。

·FALSE 、 200 、 50 为所设置控件属性的缺省值。

Page 100: 第十二章  ActiveX  控件的使用和创建

12.3.5 添加基本控件功能 控件 Squiggle 的基本功能是对鼠标单击事件作出响应,以便

生成一个新的涂鸦画面。绘制操作是在控件的成员函数 OnDra

w

中完成的,而引起 OnDraw 被调用的原因是各种需要窗口重画

的事件调用 Invalidate 函数使窗口的用户区无效。为了控制此响

应操作是否发生,避免在非鼠标单击引起的调用 OnDraw 绘制

新的涂鸦画面,需要为控件类 CSquiggleCtrl 添加一个控制绘制

操作的另一个布尔型数据成员 m_bGenNewDrawing ,同时修改

CSquiggleCtrl 的构造函数和 OnDraw 函数,并添加鼠标单击事件

的映射和响应函数来实现控件的基本功能。具体步骤如下:

Page 101: 第十二章  ActiveX  控件的使用和创建

1 添加绘制新图的数据成员 m_bGenNewDrawing

class CSquiggleCtrl : public COleControl

{

DECLARE_DYNCREATE(CSquiggleCtrl)

private:

BOOL m_bGenNewDrawing;

};

Page 102: 第十二章  ActiveX  控件的使用和创建

2 修改 CSquiggleCtrl 的构造函数

在原来的构造函数中添加对包括 m_bGenNewDrawing 在内的

数据成员进行初始化。CSquiggleCtrl::CSquiggleCtrl()

{

InitializeIIDs(&IID_DSquiggle, &IID_DSquiggleEvents);

// TODO: Initialize your control's instance data here.

m_bGenNewDrawing = TRUE;

}

Page 103: 第十二章  ActiveX  控件的使用和创建

3 修改成员函数 OnDraw

使绘制新图的操作在 m_bGenNewDrawing 的控制之下进行。void CSquiggleCtrl::OnDraw(CDC* pdc, const CRect& rcBounds,

const CRect& rcInvalid)

{ // TODO: Replace the following code with your own drawing code.

// Do we need to generate a new drawing?

if(m_bGenNewDrawing)

{

m_maDrawing.SetRect(rcBounds); // Set the drawing area

m_maDrawing.ClearDrawing(); // Clear out the old drawing

m_maDrawing.NewDrawing(); // Generate the new drawing

m_bGenNewDrawing = FALSE; // Reset the control flag

}

Page 104: 第十二章  ActiveX  控件的使用和创建

// Fill in the background

pdc->FillRect(rcBounds,

Brush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));

m_maDrawing.Draw(pdc); // Draw the squiggle drawing

}

4 添加鼠标单击响应

· 添加库存事件 click

在 Class View 中, DSquiggleEvents 或 CSquiggleCtrl 处单击鼠标

右键,并在弹出的环境菜单中选择 “ Add Event…” 菜单项。在

弹出的对话框 “ Add Event” 中进行所需定制事件的添加操作。

在 Visual c++ 6.0 中,添加操作如下:

Page 105: 第十二章  ActiveX  控件的使用和创建

在 Visual c++ .NET 中,添加操作如下:

Page 106: 第十二章  ActiveX  控件的使用和创建

· 添加鼠标单击响应函数 在 ClassWizard 中选择 Message Maps 属性页,添加鼠标单击响 应函数 OnClick ,注意,在 Visual C++ .NET 中,是通过重定义

基类虚函数 OnClick 完成的。该函数的操作代码如下:

Page 107: 第十二章  ActiveX  控件的使用和创建

void CSquiggleCtrl::OnClick(USHORT iButton)

{

// TODO: Add your specialized code here and/or call the base class

// Can we generate a new drawing?

if(!m_keepCurrentDrawing)

{ // Set the flag so a new drawing will be generated

m_bGenNewDrawing = TRUE;

// Invalidate the control to trigger the OnDraw function

Invalidate();

}

COleControl::OnClick(iButton);

}

Page 108: 第十二章  ActiveX  控件的使用和创建

12.3.6 添加控件方法· 添加定制方法 DoClick 添加此函数的目的是使控件的应

用程序可以模拟控件的鼠标单击事件。添加的方法:在 Visual C++ 6.0 中: 在 Visual C++ 6.0 中:

Page 109: 第十二章  ActiveX  控件的使用和创建

在弹出的 “ Add Method” 对话框中,进行如下图所示的操作。

在 Visual c++ 6.0 中:

Page 110: 第十二章  ActiveX  控件的使用和创建

在 Visual c++ .NET 中:

该函数的操作代码如下:

Page 111: 第十二章  ActiveX  控件的使用和创建

在 Visual c++ 6.0 中:void CSquiggleCtrl::DoClick() { COleControl::DoClick(); }

在 Visual c++ .NET 中:void CSquiggleCtrl::DoClick()

{

AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 缺省代码 COleControl::DoClick();

}

· 添加定制方法 SaveDrawing

添加此方法的目的是使应用程序可以通过 CModArt 的持续化 函数,将当前绘制的涂鸦画面保存到一个指定的磁盘文件 中,添加方法与 DoClick 类同。在 “ Add Method” 对话框中,进 行如下图所示的操作。

Page 112: 第十二章  ActiveX  控件的使用和创建

在 Visual c++ 6.0 中:

Page 113: 第十二章  ActiveX  控件的使用和创建

在 Visual c++ .NET 中:

该函数的操作代码如下:在 Visual c++ 6.0 中:

Page 114: 第十二章  ActiveX  控件的使用和创建

在 Visual c++ .NET 中:BOOL CSquiggleCtrl::SaveDrawing(LPCTSTR sFileName)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 缺省代码 try

{ // Create a CFile object

CFile lFile(sFileName, CFile::modeCreate | CFile::modeWrite);

// Create a CArchive object to save the file

CArchive lArchive(&lFile, CArchive::store);

m_maDrawing.Serialize(lArchive); // Load the file

FireFileStored(); // Fire the FileStored event

}

catch(CFileException err)

{ return VARIANT_FALSE; }

return VARIANT_TRUE;

}

Page 115: 第十二章  ActiveX  控件的使用和创建

BOOL CSquiggleCtrl::SaveDrawing(LPCTSTR sFileName)

{

try

{ // Create a CFile object

CFile lFile(sFileName, CFile::modeCreate | CFile::modeWrite);

// Create a CArchive object to save the file

CArchive lArchive(&lFile, CArchive::store);

m_maDrawing.Serialize(lArchive); // Load the file

FireFileStored(); // Fire the FileStored event

}

catch(CFileException err)

{ return FALSE; }

return TRUE;

}

Page 116: 第十二章  ActiveX  控件的使用和创建

在 Visual c++ .NET 中:VARIANT_BOOL CSquiggleCtrl::SaveDrawing(LPCTSTR sFileName)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 缺省代码 try

{ // Create a CFile object

CFile lFile(sFileName, CFile::modeCreate | CFile::modeWrite);

// Create a CArchive object to save the file

CArchive lArchive(&lFile, CArchive::store);

m_maDrawing.Serialize(lArchive); // Load the file

FireFileStored(); // Fire the FileStored event

}

catch(CFileException err)

{ return VARIANT_FALSE; }

return VARIANT_TRUE;

}

Page 117: 第十二章  ActiveX  控件的使用和创建

· 添加定制方法 LoadDrawing

添加此方法的目的是使应用程序可以通过 CModArt 的持续化

函数,将一个指定磁盘文件中的信息在控件中恢复涂鸦。添

加方法与 SaveDrawing 的添加方法相同。函数代码如下:

Page 118: 第十二章  ActiveX  控件的使用和创建

在 Visual c++ 6.0 中:BOOL CSquiggleCtrl::LoadDrawing(LPCTSTR sFileName)

{

try

{

CFile lFile(sFileName, CFile::modeRead); // Create a CFile object

CArchive lArchive(&lFile, CArchive::load);

// Create a CArchive object to load the file

m_maDrawing.Serialize(lArchive); // Load the file

m_bGenNewDrawing = FALSE;

// Make sure that the loaded drawing won't be overwritten

Invalidate(); // Draw the loaded drawing

FireFileLoaded(); // Fire the FileLoaded event

}

Page 119: 第十二章  ActiveX  控件的使用和创建

catch(CFileException err)

{

return FALSE;

}

return TRUE;

}

Page 120: 第十二章  ActiveX  控件的使用和创建

在 Visual c++ .NET 中:VARIANT_BOOL CSquiggleCtrl::LoadDrawing(LPCTSTR sFileName)

{

AFX_MANAGE_STATE(AfxGetStaticModuleState()); // 缺省代码 try

{

CFile lFile(sFileName, CFile::modeRead); // Create a CFile object

CArchive lArchive(&lFile, CArchive::load);

// Create a CArchive object to load the file

m_maDrawing.Serialize(lArchive); // Load the file

m_bGenNewDrawing = FALSE;

// Make sure that the loaded drawing won't be overwritten

Invalidate(); // Draw the loaded drawing

FireFileLoaded(); // Fire the FileLoaded event

}

Page 121: 第十二章  ActiveX  控件的使用和创建

catch(CFileException err)

{

return VARIANT_FALSE;

}

return VARIANT_TRUE;

}

Page 122: 第十二章  ActiveX  控件的使用和创建

12.3.7 添加控件事件 方法 SaveDrawing 和 LoadDrawing 中,当操作成功实现时,将

分别发出事件消息 FireFileStored 和 FireFileLoaded ,以便通知使

用控件的应用程序。

· 添加定制事件 FileStored

在 Class View 中, DSquiggleEvents 或 CSquiggleCtrl 处单击鼠标右键,并在弹出的环境菜单中选择 “ Add Event…” 菜单项。在弹出的对话框 “ Add Event” 中进行所需定制事件的添加操作。

在 Visual c++ 6.0 中,添加操作如下:

Page 123: 第十二章  ActiveX  控件的使用和创建
Page 124: 第十二章  ActiveX  控件的使用和创建

在 Visual c++ .NET 中,添加操作如下:

· 添加定制事件 FileLoaded

添加方法与 FileStored 的添加方法相同。

Page 125: 第十二章  ActiveX  控件的使用和创建

12.3.8 编译和测试控件 选择菜单项 Build -> Build Squiggle.ocx 或 Build -> Rebuild All

“Squiggle” 项目进行编译,编译成功将在项目目录的 Dedug 子目

录中生成一个 ActiveX 控件文件 Squiggle.ocx ,并对此控件进行了

系统注册,以便能在容器应用程序中使用它(注册信息可以在

系统注册文件中通过 \ HKEY_LOCAL_MACHINE \ SOFTWARE \

CLASSES \ SQUIGGLE.Squiggle.1 \ CLSID查询到)。如果控件成功

编译了,但没有注册控件,则需要选择菜单项 Tools -> Register

Control 为已经编译成功的控件进行系统注册。)

Page 126: 第十二章  ActiveX  控件的使用和创建

选择菜单项 Tools -> ActiveX Control Test Container 便可以 V

C 环

境中使用专门的容器工具测试已经编译注册的 ActiveX 控件的各

项功能。在该测试工具环境中,执行菜单项 Edit -> Insert New

Control 命令,打开一个 “ Insert Control” 对话框,从已注册的控件

列表中选择将要被测的控件插入到容器区。如下图所示:

Page 127: 第十二章  ActiveX  控件的使用和创建
Page 128: 第十二章  ActiveX  控件的使用和创建

在控件被插入测试容器后,就可以对它进行各种操作了,如

改变它的大小、单击控件、保存涂鸦、恢复涂鸦等。如果触发

了该控件中的任何一个事件,就会在测试容器的底部窗格中看

到控件所触发的那个事件。如下图所示。

Page 129: 第十二章  ActiveX  控件的使用和创建
Page 130: 第十二章  ActiveX  控件的使用和创建

对于所选控件,在测试容器的菜单中选择 Edit -> Properties ,

就会打开被测控件的属性页,它允许对控件的各种属性进行修

改,如下图所示。

Page 131: 第十二章  ActiveX  控件的使用和创建

为了测试添加到控件中的各种方法,在测试容器的菜单中选

择 Control -> Invoke Methods ,将会打开一个“ Invoke Methods”

话框,在此对话框中,可以从控件中可用方法列表中进行选择

被测方法,并输入该方法所需要的参数,然后按《 Invoke》按

钮,以便观察控件由于该方法的调用所引起的响应,参见下图

所示。

Page 132: 第十二章  ActiveX  控件的使用和创建
Page 133: 第十二章  ActiveX  控件的使用和创建

12.3.9 创建一个使用控件 Squiggle 的应用实例1 创建应用程序项目

使用 AppWizard 创建命名为 “ Test” 的 Dialog based 应用程序项

目,选择 English 为资源语言。

2 为项目添加 ActivX 控件 Squiggle

在 Visual C++ 6.0 中,选择 Project -> Add To Project -> Comp

onents

and Controls… 菜单项,打开对话框 “ Components and Controls

Gallery” ,在对话框中选择 Registered ActiveX Controls 后,按

《 Insert》按钮,以便从已经注册的 ActiveX 控件列表中搜寻

Squiggle ,从而完成向项目中添加控件。

Page 134: 第十二章  ActiveX  控件的使用和创建

在 Visual C++ .NET 中,是通过在 Toolbox 中,单击鼠标右键,并在弹出的环境菜单中选择 “ Choose Items…” 菜单项。然后在弹出的对话框中将已经注册的 ActiveX 控件 Squiggle 选入当前的 Toolbox 中。

3 编辑主对话框模板

打开主对话框模板 IDD_TEST_DIALOG ,删除模板中的原有控

件,加入一个 ActiveX 控件 Squiggle 、四个按钮控件、三个静

态文本和只读文本编辑框,分布如下:

Page 135: 第十二章  ActiveX  控件的使用和创建

各控件的类型、标识和标题如下表所示:

Page 136: 第十二章  ActiveX  控件的使用和创建

控件类别 控件标识 控件标题Suiggle

Push button

Push button

Push button

Push button

Static Box

Static Box

Static Box

Edit Box

Edit Box

Edit Box

Static Box

Static Box

Edit Box

Edit Box

Check button

IDC_SQUIGGLECTRL

IDC_STORE

IDC_RECOVER

IDC_DRAW

IDC_EXIT

IDC_STATIC

IDC_STATIC

IDC_STATIC

IDC_NUM_CLICK

IDC_NUM_STORE

IDC_NUM_LOAD

IDC_STATIC

IDC_STATIC

IDC_SQUIGGLE_LENGTH

IDC_SQUIGGLE_NUM

IDC_KEEP_DRAWING

&STORE

&RECOVER

&DRAW

E&XIT

“单击次数:”“存储次数:”“装载次数:”

“涂鸦长度:”“涂鸦段数:”

“保持画面”

Page 137: 第十二章  ActiveX  控件的使用和创建

4 为 CTestDlg类增加控件 Squiggle 对象 使用 ClassWizard 为 CTestDlg 类添加与 IDC_SQUIGGLEC

TRL 关 联的对象成员 m_ctrlSquiggle 。class CTestDlg : public CDialog

{ // Construction

public:

CTestDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data

//{{AFX_DATA(CTestDlg)

enum { IDD = IDD_TEST_DIALOG };

CSquiggle m_ctrlSquiggle;

//}}AFX_DATA

};

Page 138: 第十二章  ActiveX  控件的使用和创建

5 为 CTestDlg 添加一些按钮控件消息响应函数

这些消息响应函数的定义代码如下:

控件标识 响应函数 操作说明

IDC_STORE

IDC_RECOVER

IDC_DRAW

IDC_EXIT

IDC_SQUIGGLE_LENGTH

IDC_SQUIGGLE_NUM

IDC_KEEP_DRAWING

OnStore

OnRecover

OnDraw

OnExit

OnChangeSquiggleLength

OnChangeSquiggleNum

OnKeepDrawing

保存当前涂鸦画面

从保存的文件中恢复保存的涂鸦画面

生成并绘制新的涂鸦画面

退出应用程序

修改每条涂鸦线的最大长度

修改每次绘制的涂鸦线的最多条数

选择保持 /修改当前的涂鸦画面

Page 139: 第十二章  ActiveX  控件的使用和创建

void CTestDlg::OnStore()

{

if(!m_ctrlSquiggle.SaveDrawing(_T("test")))

AfxMessageBox(_T("Storing Drawing fails"));

}

void CTestDlg::OnRecover()

{

if(!m_ctrlSquiggle.LoadDrawing(_T("test")))

AfxMessageBox(_T("Loading Drawing fails"));

}

void CTestDlg::OnDraw()

{

m_ctrlSquiggle.DoClick();

}

Page 140: 第十二章  ActiveX  控件的使用和创建

void CTestDlg::OnExit()

{

OnOK();

}

void CTestDlg::OnChangeSquiggleLength()

{

m_ctrlSquiggle.SetSquiggleLength(

GetDlgItemInt(IDC_SQUIGGLE_LENGTH));

}

void CTestDlg::OnChangeSquiggleNum()

{

m_ctrlSquiggle.SetNumberSquiggles(

GetDlgItemInt(IDC_SQUIGGLE_NUM));

}

Page 141: 第十二章  ActiveX  控件的使用和创建

void CTestDlg::OnKeepDrawing()

{

m_ctrlSquiggle.SetKeepCurrentDrawing(

((CButton*)GetDlgItem(IDC_KEEP_DRAWING))->GetCheck());

}

Page 142: 第十二章  ActiveX  控件的使用和创建

6 为 CTestDlg 添加响应控件 Squiggle 事件的处理函数

这些消息响应函数的定义代码如下:void CTestDlg::OnClickSquigglectrl()

{

m_nClick++;

UpdateData(FALSE);

}

事件名 响应函数 操作说明

click

FileStroed

FileLoaded

OnClickSquigglectrl

OnFileStoredSquigglectrl

OnFileLoadedSquigglectrl

单击事件计数器 m_nClick增 1 ,并显示

存储事件计数器 m_nStore增 1 ,并显

装载事件计数器 m_nStore增 1 ,并显

Page 143: 第十二章  ActiveX  控件的使用和创建

void CTestDlg::OnFileLoadedSquigglectrl()

{

m_nLoad++;

UpdateData(FALSE);

}

void CTestDlg::OnFileStoredSquigglectrl()

{

m_nStore++;

UpdateData(FALSE);

}

Page 144: 第十二章  ActiveX  控件的使用和创建

7 在 OnInitDialog 中添加初始化代码BOOL CTestDlg::OnInitDialog()

{ …

// TODO: Add extra initialization here

SetDlgItemInt(IDC_SQUIGGLE_LENGTH,

m_ctrlSquiggle.GetSquiggleLength());

SetDlgItemInt(IDC_SQUIGGLE_NUM,

m_ctrlSquiggle.GetNumberSquiggles());

((CButton*)GetDlgItem(IDC_KEEP_DRAWING))->

SetCheck(m_ctrlSquiggle.GetKeepCurrentDrawing());

return TRUE; // return TRUE unless you set the focus to a control

}

8. 编译运行 “ Test”