VS2010下通过时钟控件学习ActiveX控件编程+js关联ActiveX控件事件方法

一、ActiveX控件简介

1、什么是ActiveX控件

-------百度吧。ActiveX控件基于COM,但是即使不懂COM编程技术,仍然可以编写ActiveX控件。

一个典型的ActiveX控件具有:方法、属性、事件

2、ActiveX控件有什么用途?

在实际编程工作中,可以将一些常用的功能封装在一个ActiveX控件中,提供给开发人员使用。

ActiveX控件不能独立运行,需嵌入容器程序中才能运行。典型的应用比如windows窗口程序、Web应用程序。

ActiveX控件通常后缀格式为ocx或者dll。控件使用之前需要注册,方法如下:

//注册
regsvr32 xxx.ocx

//取消注册
regsvr32 /u xxx.ocx

二、利用vs2010编写时钟控件

创建工程——》添加属性——》添加方法——》添加事件——》测试

1、创建工程

a、新建MFC ActiveX应用程序,使用默认设置即可。新建完成通过类视图可以看到,自动生成的3个类如下图:

VS2010下通过时钟控件学习ActiveX控件编程+js关联ActiveX控件事件方法_第1张图片

        CClockOCXAPP类派生于COleControlModule类,而COleControlModule类又派生于CWinApp,所以把该类看作应用程序类,它的实例表示了控件本身。

CClockOCXCtrl类派生于COleControl类,而COleControl类派生于CWnd类,因此,该类是一个窗口类,对控件的操作都在该类中实现。

CClockPropPage类派生于COleProperty类,而COleProperty类派生于CDialog类,所以该类是用来显示口案件属性页的,对应一个对话框资源与之关联。

另外,_DClockOCX和_DClockOCXEvents是控件与外部程序通信的接口,就是说容器类程序要调用该控件就是通过这个接口。CClockOCXCtrl类继承_DClockOCX接口。

b、开始进入正题

程序结构分析完之后,开始实现功能。

要实现时钟显示功能,分两步:第一步,处理控件的WM_CREATE消息,即在OnCreate()函数中启动计时器。第二步,处理WM_TIMER消息,即计时增加1s(OnCreate函数中设置),重绘窗口,显示时间,重绘窗口时调用OnDraw()方法。

。在CClockOCXCtrl类中修改OnDraw方法,默认是画了一个椭圆,将格式化的时间字符串绘制到窗口。如下:

void CClockOCXCtrl::OnDraw(
			CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
	if (!pdc)
		return;

	// TODO: 用您自己的绘图代码替换下面的代码。
// 	pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
// 	pdc->Ellipse(rcBounds);

	CTime time = CTime::GetCurrentTime();//获得系统时间
	CString str = time.Format("%H:%M:%S");
	pdc->TextOut(0,0,str);
}

。打开类向导(在类上右击,选择类向导),在CClockOCXCtrl类添加WM_CREATE和WM_TIMER消息处理函数

int CClockOCXCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (COleControl::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	SetTimer(1,1000,NULL);//设置定时器,1s显示一次
	return 0;
}
void CClockOCXCtrl::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	/*AmbientUserMode()返回0,处于设计模式,否则处于运行模式*/
	if(AmbientUserMode())//在设计模式下,让时钟静止,即不重绘窗口
	{
		Invalidate();//使窗口无效,从而使窗口重绘
	}
	COleControl::OnTimer(nIDEvent);
}

2、添加属性

.基本功能实现了,下面增加一些人性化的实现,比如设置控件的属性,设置显示间隔,比如2s/3s显示一次等

。增加设置时钟的背景色和前景色(标准属性)

转到类视图中,选中_DClockOCX,右键,添加属性,名字选择BackColor,ForeColor,就可以看到_DClockOCX多了两个属性,这些事系统自带的标准属性,还包括font等

设置好属性之后,还需在OnDraw()函数中添加如下代码:

//前景色和背景色是activex控件的标准属性
	CBrush brush(TranslateColor(GetBackColor()));
	pdc->FillRect(rcBounds,&brush);
	pdc->SetBkMode(TRANSPARENT);//文字背景设置为透明
	pdc->SetTextColor(TranslateColor(GetForeColor()));


添加上述代码还不够,因为添加之后,属性页就增加了一页,所以还要在CClockOCXCtrl.cpp中修改”属性页“:

// 属性页

// TODO: 按需要添加更多属性页。请记住增加计数!
BEGIN_PROPPAGEIDS(CClockOCXCtrl, 2)
	PROPPAGEID(CClockOCXPropPage::guid)
	PROPPAGEID(CLSID_CColorPropPage);//增加颜色属性页
END_PROPPAGEIDS(CClockOCXCtrl)
。自定义属性,增加显示间隔设置

同样转到类视图中,选中_DClockOCX,右键,添加属性,如下图所示:

VS2010下通过时钟控件学习ActiveX控件编程+js关联ActiveX控件事件方法_第2张图片

属性名是外部名称,自动生成内部变量以及通知函数。

实现OnIntervalChanged()函数如下,根据用户设置的间隔来显示:

void CClockOCXCtrl::OnIntervalChanged(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	// TODO: 在此添加属性处理程序代码
	if (m_Interval < 0 || m_Interval > 6000)
	{
		m_Interval = 1000;
	}
	else
	{
		m_Interval = m_Interval/1000 *1000;
	}
	KillTimer(1);
	SetTimer(1,m_Interval,NULL);
	SetModifiedFlag();
}

。下面想通过控件的属性表单设置来设置Interval的值

打开资源视图,找到IDD_PROPPAGE_CLOCKOCX对话框资源,添加静态文本框以及编辑框,如下图所示:

VS2010下通过时钟控件学习ActiveX控件编程+js关联ActiveX控件事件方法_第3张图片

为新添加的编辑框添加成员变量m_updateInterval,那么怎么关联该变量与Interval变量呢?

在CClockOCXPropPage类的DoDataExchange()函数中添加如下代码:

void CClockOCXPropPage::DoDataExchange(CDataExchange* pDX)
{	
	DDP_Text(pDX, IDC_EDIT_INTERVAL, m_updateInterval,_T("Interval"));//将编辑框与外部属性关联,注意顺序
	DDX_Text(pDX, IDC_EDIT_INTERVAL, m_updateInterval);
	DDP_PostProcessing(pDX);
}
如果在vc6.0下,在添加变量时可直接选择需要关联的属性,以上代码也会自动生成,但是VS2010下没有这个选择,所以需要手动添加关联那行代码,注意顺序不能乱。

该自定义属性设置好之后,还不能达到需要的效果,因为开始我在CClockOCXCtrl::OnCreate方法中设置时间使用了固定值,所以需要修改一下该函数的实现,修改为:

SetTimer(1,m_Interval,NULL);//设置定时器

b、属性的持久性

属性的持久性指一次设置属性值之后,该属性值一直在属性页中保持不变,下次启动时属性默认值仍为之前设置的值。那么如何实现呢?

回到CClockOCXCtrl类中,有个DoPropExchange函数,该函数提供了控件属性持久性支持。设置自定义属性持久性都是调用“PX_”开头的函数实现。如下:

// CClockOCXCtrl::DoPropExchange - 持久性支持

void CClockOCXCtrl::DoPropExchange(CPropExchange* pPX)
{
	ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
	COleControl::DoPropExchange(pPX);

	// TODO: 为每个持久的自定义属性调用 PX_ 函数。
	PX_Short(pPX,_T("Internal"),m_Interval,1000);//属性的持久性,关闭工程之后属性得到保存
}
c、控件属性发生变化后,如何通知容器呢?

调用COleControl类的成员函数:void BoundPropertyChanged(DISPID dispid),其参数是属性的调度ID,这些ID可以在.idl文件中查询到,如下:

dispinterface _DClockOCX
	{
		properties:
			[id(DISPID_BACKCOLOR), bindable, requestedit] OLE_COLOR BackColor;
			[id(DISPID_FORECOLOR), bindable, requestedit] OLE_COLOR ForeColor;
			[id(1) ] SHORT Interval;
	methods:

			[id(DISPID_ABOUTBOX)] void AboutBox();
			[id(2)] void Hello(void);
	};

	//  CClockOCXCtrl 的事件调度接口

	[ 
		uuid(346C7BCF-EEC7-4772-B3A6-03682C9980FA)	
	]
	dispinterface _DClockOCXEvents
	{
		properties:
			//  事件接口没有任何属性

		methods:
			[id(DISPID_CLICK)] void Click(void);
			[id(1)] void Newminute(void);
	};
所以当属性Interval的值发生改变时,可以这样通知容器:BoundPropertyChanged(0x1); 在OnIntervalChanged()函数中添加该方法。

d、环境属性

在容器中使用ActiveX控件时,分为设计模式和运行模式。设计模式下,通过控件自身的属性表单改变控件的属性;运行模式下,通过调用控件的方法来改变控件的属性。

下面要实现在设计模式下,Clock控件显示的时间是静止的,而只有在运行模式下,时间显示才是动起来的。

对于控件,可以通过环境属性UserMode来判断当前所属的状态。所以在OnTimer()函数中增加模式判断:

if(AmbientUserMode())


3、添加方法

添加方法比较简单,同样选中_DClockOCX,右键,添加方法。下面以添加一个Hello方法为例,添加之后实现方法如下:

void CClockOCXCtrl::Hello(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	// TODO: 在此添加调度处理程序代码
	MessageBox(_T("HelloWord!"));
}
4、添加事件

事件和属性一样,分为标准事件和自定义事件,标准事件的触发过程被屏蔽了,比如click事件。而自定义事件触发过程需要自己实现。

a、添加标准事件(单击事件)

选中CClockOCXCtrl类,右键,添加事件鼠标单击事件,事件名称选择Click即可。这时就可以在_DClockOCXEvents接口中增加了一个Click方法。

现在我好奇的是为什么该方法不增加到_DClockOCX接口呢?很显然_DClockOCX和_DClockOCXEvents有区别,那区别在哪里呢?

首先看一下Clock.idl如下定义:

//  CClockOCXCtrl 的类信息
	[
		uuid(33E08641-67AD-48DA-9B03-EE694A20B51F)
	]
	coclass ClockOCX
	{
		[default] dispinterface _DClockOCX;
		[default, source] dispinterface _DClockOCXEvents;
	};
可以看到_DClockOCXEvents接口前面有“source”标志,表明_DClockOCXEvents是一个源接口。源接口表示控件使用这个接口来发送通知事件,这个接口不由控件来实现,实际上是由显示控件的容器来实现,容器通过一种机制知道控件中定义的源接口,于是它就实现该接口。

b、自定义事件

步骤如上,我们添加一个Newminute事件,即计时一分钟时出发事件。同样在_DClockOCXEvents接口中增加了方法Newminute(),同时还在CClockOCXCtrl类中增加了方法,如下:

//自定义事件
	void Newminute(void)
	{
		FireEvent(eventidNewminute, EVENT_PARAM(VTS_NONE));
	}
那么何时触发这个事件呢?在OnDraw函数中,获得当前时间之后,添加如下触发事件代码:

f(0 == time.GetSecond())//触发自定义事件
		Newminute();

4、测试控件

测试该时钟控件方式是将其嵌入到容器类程序中。

a、使用VS自带的TSTCON32.exe测试程序

vs2010安装目下的Samples目录下,找到VC2010Samples.zip\C++\MFC\ole\TstCon,用vs打开其解决方案,编译即可生成TestCon测试程序。

VC6.0安装后在其Tools目录下既有TSTCON32.exe测试程序

打开TSTCON32.exe测试程序,Edit-》Insert New Control,选择已经注册好的ClockOCX控件,注意VS生成控件后,由于权限问题可能会注册失败,可以手动注册,即一开始使用的注册方法。

VS2010下通过时钟控件学习ActiveX控件编程+js关联ActiveX控件事件方法_第4张图片

此时时钟处于静止状态,因为默认为设计模式,选择option,去掉勾选Design Mode,时钟开始以s计时,每秒显示一次。单击控件显示单击事件。

选中控件,选择属性,弹出属性设置对话框,如下图:

VS2010下通过时钟控件学习ActiveX控件编程+js关联ActiveX控件事件方法_第5张图片

此时Inerval的默认是已经变为1000,即1s,这是调用PX_Short(pPx,_T("Interval"),m_Interval,1000)设置的默认值。如果设置为3000,点击应用,即可看到控件每计时3s显示一次。还可以设置控件的背景色、前景色。

b、使用VS编写测试程序测试该控件

新建一个基于对话框的测试程序,转到资源视图,点开Dialog,在资源对话框上右击,插入ActiveX控件,选择之前注册好的ClockOCX控件,就可以看到显示了ClockOCX控件,可以设置属性,还可以添加事件处理函数,如下图所示:

VS2010下通过时钟控件学习ActiveX控件编程+js关联ActiveX控件事件方法_第6张图片

可以看到有两个事件:Click和Newminute事件,可以为其添加处理函数。当然还可以将该控件添加到VS的工具箱,这样就可以直接拖拽控件了。

c、浏览器调用控件测试

IE内核的浏览器是支持ActiveX控件,编写html测试代码如下:



	
		 New Document 
		
		
		
		
	
	
	
	 
		
		

时钟显示:

结果如下:

VS2010下通过时钟控件学习ActiveX控件编程+js关联ActiveX控件事件方法_第7张图片


至于浏览器处理控件事件,后面再研究一下补充上。。


补充上事件处理方法:

此处事件处理主要使用了Javascript关联ActiveX控件的方法

总共添加了3个事件,所以处理了3个事件。当然事件增加了一个返回参数,即返回当前的时间值。代码如下:



	
		 New Document 
		
		
		
		
	
	
	
	
	
	
	
	 
		
		

时钟显示:

结果:

VS2010下通过时钟控件学习ActiveX控件编程+js关联ActiveX控件事件方法_第8张图片


可以成功处理事件,但是有个问题,第一次触发Newminute事件时,textarea区会显示很多行相同的信息,后面就一行一行显示。查了很久也不知道问题出在哪,所以用了个alert(times)来弹出显示时间,点击确定之后就一行一行显示。这个问题留给大神解决吧~~~~

你可能感兴趣的:(C/C++/VC)