一、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个类如下图:
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()));
// 属性页
// TODO: 按需要添加更多属性页。请记住增加计数!
BEGIN_PROPPAGEIDS(CClockOCXCtrl, 2)
PROPPAGEID(CClockOCXPropPage::guid)
PROPPAGEID(CLSID_CColorPropPage);//增加颜色属性页
END_PROPPAGEIDS(CClockOCXCtrl)
。自定义属性,增加显示间隔设置
同样转到类视图中,选中_DClockOCX,右键,添加属性,如下图所示:
属性名是外部名称,自动生成内部变量以及通知函数。
实现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对话框资源,添加静态文本框以及编辑框,如下图所示:
为新添加的编辑框添加成员变量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();
测试该时钟控件方式是将其嵌入到容器类程序中。
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生成控件后,由于权限问题可能会注册失败,可以手动注册,即一开始使用的注册方法。
此时时钟处于静止状态,因为默认为设计模式,选择option,去掉勾选Design Mode,时钟开始以s计时,每秒显示一次。单击控件显示单击事件。
选中控件,选择属性,弹出属性设置对话框,如下图:
此时Inerval的默认是已经变为1000,即1s,这是调用PX_Short(pPx,_T("Interval"),m_Interval,1000)设置的默认值。如果设置为3000,点击应用,即可看到控件每计时3s显示一次。还可以设置控件的背景色、前景色。
b、使用VS编写测试程序测试该控件
新建一个基于对话框的测试程序,转到资源视图,点开Dialog,在资源对话框上右击,插入ActiveX控件,选择之前注册好的ClockOCX控件,就可以看到显示了ClockOCX控件,可以设置属性,还可以添加事件处理函数,如下图所示:
可以看到有两个事件:Click和Newminute事件,可以为其添加处理函数。当然还可以将该控件添加到VS的工具箱,这样就可以直接拖拽控件了。
c、浏览器调用控件测试
IE内核的浏览器是支持ActiveX控件,编写html测试代码如下:
New Document
时钟显示:
结果如下:
至于浏览器处理控件事件,后面再研究一下补充上。。
补充上事件处理方法:
此处事件处理主要使用了Javascript关联ActiveX控件的方法
总共添加了3个事件,所以处理了3个事件。当然事件增加了一个返回参数,即返回当前的时间值。代码如下:
New Document
时钟显示:
结果:
可以成功处理事件,但是有个问题,第一次触发Newminute事件时,textarea区会显示很多行相同的信息,后面就一行一行显示。查了很久也不知道问题出在哪,所以用了个alert(times)来弹出显示时间,点击确定之后就一行一行显示。这个问题留给大神解决吧~~~~