n 容器应用程序是可以嵌入或链接对象的应用程序。Word就是容器应用程序。
n 服务器应用程序是创建对象并且当对象被双击时,可以被启动的应用程序。Excel就是服务器应用程序。
ActiveX控件的四种属性
n Stock:为每个控件提供的标准属性,如字体或颜色。
n Ambient:围绕控件的环境属性——已被置入容器的属性。这些属性不能被更改,但控件可以使用它们调整自己的属性。
n Extended:这些是由容器处理的属性,一般包括大小和在屏幕上的位置。
n Custom:由控件开发者添加的属性。
切换到VB的运行环境中。VB做为ACTIVEX控件测试容器;
VB中加载ACTIVEX控件:选择工程下面的组件:
选择MICROSOFT WINSOCK CONTROL 6.0
(。OCX是ACTIVEX控件的后缀名,但是ACTIVEX控件的后缀名不一定是.OCX,也可以是其它的名字,比如。DLL),选上后,点确定。
选择VIEW下的OBJECT BROWSER。在所有库的下拉列表中,选择MSWINSOCKLIB。可以看到WINSOCK控件出来了。其中,绿色控件表示方法。小手表示控件属性。闪电表,示事件。一个典型ACTIVEX控件会有这三个属性。ACTIVEX控件好处:把常用的功能封装到ACTIVEX控件中,提供给程序开发使用。
新建一个工程,选择MFC的MFC ACTIVEX CONTROLWIZARD,名字是TEST。(开发一个时钟控件)。保持缺省,完成。
平放的小勺是接口。外部的函数通过接口去访问控件的属性和方法。在接口中定义的所有函数都是纯虚函数。通过接口调用的函数是调用CTestCtrl中真正实现的函数。编译一个,会在DEBUG目录下有一个TEST.OCX,这就是控件,要用的话,把它放到要用的目录下面。不能!运行程序,因为它必须要有一个容器。选择ACTIVEX CONTROL TEST CONTAINER。
选择EDIT下面的INSERT NEW CONTROL…….出现下面的图,选择我们的TEST CONTROL。(VC编译环境中)
下面是VB环境中:
ACTIVEX要先注册,再能使用。
要想删除ACTIVEX的注册信息:
运行regsvr32 /u(/u表示反注册,然后将TEST.OCX拉到regsvr32 /u后面),弹出:
这时候就找不到TEST ACTIVEX控件了。
要想再注册,用上面同样的方法,但是/u要取消。这时弹出的消息如下 :
这时再找,就可以找到TEST.OCX了。
在VC中的TOOLS菜单下的REGISTER CONTROL也可以达到上面的效果。
控件使用之间,都要注册。
在控件上面输出当前时间。
代码如下:
void CTestCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
// TODO: Replace the following code with your own drawing code.
CTime t = CTime::GetCurrentTime();
CString strtime=t.Format("%H:%M:%S");
pdc->TextOut(0,0,strtime);
//系统下面自动加的这两句代码是为了填充背景和画椭圆的。
//pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
//pdc->Ellipse(rcBounds);
}
编译的时候要先将测试中的TEST控件删除。
上面这段代码的时间是静止的,不会动,下面创建一个动态的时钟。
int CTestCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
SetTimer(1,1000,NULL);
return 0;
}
void CTestCtrl::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
Invalidate();
下面这句也可以:
InvalidateControl();
二者等效。
COleControl::OnTimer(nIDEvent);
}
这时时间动起来了。
下面为控件添加设置前景色和背景色。
然后再增加前景色。
void CTestCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
// TODO: Replace the following code with your own drawing code.
COLORREF bkcolor=TranslateColor(GetBackColor( ));
CBrush brush(bkcolor);
pdc->FillRect(rcBounds,&brush);
COLORREF txcolor=TranslateColor(GetForeColor( ));
pdc->SetTextColor(txcolor);
pdc->SetBkMode(TRANSPARENT);
CTime t = CTime::GetCurrentTime();
CString strtime=t.Format("%H:%M:%S");
pdc->TextOut(0,0,strtime);
//系统下面自动加的这两句代码是为了填充背景和画椭圆的。
//pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
//pdc->Ellipse(rcBounds);
}
通常一个ACTIVEX控件会提供自己属性页。
在TestCtl.cpp中:
BEGIN_PROPPAGEIDS(CTestCtrl, 2)
PROPPAGEID(CTestPropPage::guid)//guid表示全局唯一标识符。
PROPPAGEID(CLSID_CColorPropPage)//颜色属性页
END_PROPPAGEIDS(CTestCtrl)
这时属性页就出现了。
下面给控件添加一个自定义的属性:
增加一个时间间隔的属性。
void CTestCtrl::OnIntervalChanged()
{
// TODO: Add notification handler code
if (m_interval<0 || m_interval>6000)
{
m_interval=1000;
}
else
{
m_interval=m_interval/1000*1000;//将用户输入取整,2345-》2000.
KillTimer(1);//销毁先前的定时器
SetTimer(1,m_interval,NULL);//设置新的定时器
}
SetModifiedFlag();
}
接下来将自定义的属性在属性表单中进行设置。
在IDD_PROPPAGE_TEST中:
并为EDIT它关联一个short型的m_timeinterval;
下面的interval是外部名称。
下面是运行结果:
下面给控件添加方法:
void CTestCtrl::Hello()
{
// TODO: Add your dispatch handler code here
MessageBox("hello world!");
}
接下来给控件添加一个事件:
[ uuid(00E87A5D-986C-4FF9-BE79-C3F0F96AA13C),
helpstring("Test Control"), control ]
coclass Test
{
[default] dispinterface _DTest;
[default, source] dispinterface _DTestEvents;//表示控件用这个接口来发送通知事件,这个接口不是控件本身实现的接口。
};//source表示_DTestEvents是一个源接口,是由窗口来实现的。
这时测试:
下面增加一个自定义的事件:
在_DTestEvents上右击弹出下面对话框:
在void CTestCtrl::OnDraw中添加:
if (0==t.GetSecond())
{
FireNewminute();
}
测试结果:
新的一分钟到达时,事件发送了。
当添加自定义的事件时,一定要在事件发生时,去发出事件通知。标准事件时,MFC自动帮我们实现。
将文件设置前景色,背景色,时间间隔,然后保存,再次打开,发现时间间隔没有保存下来(前景色和背景色都保持下来),说明时间间隔没有持久性。
void CTestCtrl::DoPropExchange(CPropExchange* pPX)//此函数是做持久性的。
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// TODO: Call PX_ functions for each persistent custom property.
PX_Short(pPX, "interval", m_interval,1000);
}
int CTestCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
SetTimer(1,m_interval,NULL);
return 0;
}
接下来再看一个问题:
在VB中做测试,当通过属性页改变时间间隔时,VB窗口中的属性时间间隔并没有随之改变。这主要是因为自定义的属性在发生改变时,没有通知容器,让容器调整。
由上图可以看出,间隔的属性调度ID为1.
void CTestCtrl::OnIntervalChanged()
{
// TODO: Add notification handler code
if (m_interval<0 || m_interval>6000)
{
m_interval=1000;
}
else
{
m_interval=m_interval/1000*1000;//将用户输入取整,2345-》2000.
KillTimer(1);//销毁先前的定时器
SetTimer(1,m_interval,NULL);//设置新的定时器
BoundPropertyChanged(1);
}
SetModifiedFlag();
}
在VB中,可以在设计时,也可以在运行时改变控件的属性。在设计时,时间值不动,在运行时,时间值才改变。容器知道自己是在设计是还是在运行时。
void CTestCtrl::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
if(AmbientUserMode())//运行时
{
InvalidateControl();
}
COleControl::OnTimer(nIDEvent);
}
当编写完ACTIVEX后,可以选择
编译,就可以生成一个发行版的OCX控件,在发行模式下进行编译时,VC程序会做一些优化,同时生成的可执行文件比较小。下面做一个VC的测试程序,用来访问我们做的ACTIVEX控件。
新建一个MFC的对话框程序(TestTest)
在对话框上点击右键,插入ACTIVEX控件,选择我们刚刚建立的TEST控件。放置一个,然后右击它,可以看到相应的属性。
还可以通过工程下面的添加到工程(组件和控件),选择ACTIVEX
上面都是通过静态增加的,下面动态增加一个。在对话框上面增加一个按钮,当点击这个按钮的时候,动态的增加一个时钟控件。
为CTestTestDlg增加:
private:
CTest m_clock;
在头文件中:#include "test.h"
void CTestTestDlg::OnButton1()
{
// TODO: Add your control notification handler code here
m_clock.Create("windowname",WS_VISIBLE|WS_CHILD,CRect(0,0,100,70),this,123);
m_clock.Hello();
m_clock.SetBackColor(RGB(255,0,0));
m_clock.SetForeColor(RGB(0,255,0));
m_clock.SetInterval(m_clock.GetInterval()*2);
}
下面是对事件进行响应:
void CTestTestDlg::OnnewminuteTestctrl3()
{
// TODO: Add your control notification handler code here
MessageBox("newminute!");
}
对于一个动态创建的控件,去响应它的事件对象:
在创建的时候,给它分配了一个ID号,根据这个ID号,自己写。
下面是我写的:
由于创建时,控件ID选为123,修改代码如下:
TestTestDlg.h中:
afx_msg void Onnewminute123();
在TestTestDlg.cpp中:
BEGIN_EVENTSINK_MAP(CTestTestDlg, CDialog)
//{{AFX_EVENTSINK_MAP(CTestTestDlg)
ON_EVENT(CTestTestDlg, IDC_TESTCTRL3, 1 /* newminute */, OnnewminuteTestctrl3, VTS_NONE)
ON_EVENT(CTestTestDlg, 123, 1 /* newminute */, Onnewminute123, VTS_NONE)
//}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()
void CTestTestDlg::Onnewminute123()
{
MessageBox("newminute123!");
}
注:
1。不用注册控件,在VB和VC中都能看到产生的.ocx控件,只有当显示的调用regsvr32 /u时,在VB和VC中不能看到控件了。
2.在OnDraw函数中设置了背景色和前景色后,在VB面板中可以看到这两个属性并进行设置了(可以生效)。pdc->SetBkMode
(TRANSPARENT);在pdc->SetTextColor(TranslateColor(GetForeColor()));这句前面还是后面无所谓。
3.PROPPAGEID(CLSID_CColorPropPage)是给ACTIVEX控件添加(系统定义好的)属性页。增加一个时间间隔的属性(自定义的属性
页)
4.添加系统事件时直接添加,不需要写代码。添加自定义的事件时,要在事件发生时,去发出事件通知。通知名字就是外部名字。
5.PX_Short(pPX, "interval", m_interval,1000);BoundPropertyChanged();
6.AmbientUserMode( );
7.要动态的添加控件,也要先静态的把这个控件加入到MFC程序中。
8.例子中实现的是对123控件当新的一分钟到来时的响应。如果这个控件ID不是123,而是ID_形式,则在resource.h中要定义它值。
若写click事件,可参照一个现成的来写。
9.为时钟控件添加的属性有:前景色(ForeColor)和背景色(BackColor)(通过Add Property添加,标准属性(Stock),函数
GetBackColor和SetBackColor都是系统自动加上去的,并且系统提供系统画面和串行化操作)。自定义属性interval(通过Add
Property添加,类型为short,Variable name为m_interval,Notification是系统自动加的:OnIntervalChanged,Implementation为
Member variable),要自己写代码、加属性页(加属性页时,关联的成员变量名字要和自定义的属性名字一样,就可以实现系统自
动关联)和实现串行化工作。添加方法时,选择Add Method.然后为方法添加响应函数。为控件添加事件时,选择ActiveX Events
的Add Event.可以添加系统的事件,也可以添加自定义的事件(当添加自定义的事件时,一定要在事件发生时,去发出事件通知。
标准事件时,MFC自动帮我们实现)。
10.将时间间隔持久性:DoPropExchange中的PX_来实现。通知容器:BoundPropertyChanged。设计还是运行时判断函数:AmbientUserMode。