ATL7.1编写ActiveX控件
转载我们创建dll形式的ATL项目EllipseCase 如下图:
我打算创建一个Ellipse控件,该控件拥有背景属性,可以改变背景颜色。
该控件绘制一个笛卡尔坐标系,用户可以通过右键菜单修改X、Y、Z轴的颜色。
好啦,现就这么多了,让我上路吧。
重要提醒:如果你要创建一个能够处理WM_CREATE消息的ActiveX控件,请注意:
必须------
a) 选择标准控件
b) 选中仅适用于有窗口的
c) 在构造函数中加上m_bWindowOnly=TRUE。
其中的原因在于符合控件派生自CComCompositeControl<>模板类,而标准控件派生自CComControl<>类。派生自CComCompositeControl类意味着该类可以包容其它的控件,但是同时具有了对话框的特征,且拥有了自己的对话框资源。所以如果想要添加初始化代码,应该处理WM_INITDIALOG消息。如果我们想在自己的控件中包含另一个ActiveX控件,我们应该创建复合控件,并用对话框承载想要包含的ActiveX控件。
我们先停下来,了解一下ActiveX控件的基础知识,并理解向导生成的代码。
看看CEllispe的父类
class ATL_NO_VTABLE CEllispe :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CEllispe, &CLSID_Ellispe>,
public IPersistStreamInitImpl<CEllispe>,
public ISpecifyPropertyPagesImpl<CEllispe>,
public IPersistStorageImpl<CEllispe>,
public CStockPropImpl<CEllispe, IEllispe>,
public IOleControlImpl<CEllispe>,
public IOleObjectImpl<CEllispe>,
public IOleInPlaceActiveObjectImpl<CEllispe>,
public IOleInPlaceObjectWindowlessImpl<CEllispe>,
public IViewObjectExImpl<CEllispe>,
public ISupportErrorInfo,
public IConnectionPointContainerImpl<CEllispe>,
public IProvideClassInfo2Impl<&CLSID_Ellispe, &__uuidof(_IEllispeEvents), &LIBID_EllipseCaseLib>,
public CProxy_IEllispeEvents<CEllispe>,
public IQuickActivateImpl<CEllispe>,
public IDataObjectImpl<CEllispe>,
public CComCompositeControl<CEllispe>
ActiveX控件拥有界面,所以它只能存在于单线程套间中,所以应该用CComObjectRootEx<CComSingleThreadModel>;
ActiveX控件可以被创建,所以通过CComCoClass<CEllispe, &CLSID_Ellispe>来实现自己的类厂;
ActiveX控件至少要支持流方式保存,所以使用IPersistStreamInitImpl<CEllispe>;
VB和IE要求优先以属性包的方式保存对象,所以有的ActiveX控件也要支持
ActiveX控件支持结构化存储,这样可以被嵌入到OLE文档中,IPersistStorageImpl<CEllispe>;
ActiveX控件支持标准属性,使用CStockPropImpl<CEllispe, IEllispe>可以帮助节省时间;
ActiveX控件支持标准OLE控制功能,使用IOleControlImpl<CEllispe>;
ActiveX控件支持实地激活,使用public IOleObjectImpl<CEllispe>,public IOleInPlaceActiveObjectImpl<CEllispe>;
IOleInPlaceObjectWindowlessImpl<CEllispe>帮助实现快速高效的激活;
ActiveX控件支持按需呈现对象的视图,使用IViewObjectExImpl<CEllispe>;
ActiveX控件支持传递错误信息给调用方,而不是仅通过HRESULT,使用ISupportErrorInfo;
ActiveX控件支持事件,因此使用IConnectionPointContainerImpl<CEllispe>,IProvideClassInfo2Impl,CProxy_IEllispeEvents<CEllispe>,
ActiveX控件是复合控件,所以使用了CComCompositeControl<CEllispe>
由于我们在向导中选择了两个标准属性:BackGroundColor和BackGroundStyle,所以我们来看看idl文件中有些什么:
interface IEllispe : IDispatch{
[propput, bindable, requestedit, id(DISPID_BACKCOLOR)]
HRESULT BackColor([in]OLE_COLOR clr);
[propget, bindable, requestedit, id(DISPID_BACKCOLOR)]
HRESULT BackColor([out,retval]OLE_COLOR* pclr);
[propput, bindable, requestedit, id(DISPID_BACKSTYLE)]
HRESULT BackStyle([in]long style);
[propget, bindable, requestedit, id(DISPID_BACKSTYLE)]
HRESULT BackStyle([out,retval]long* pstyle);
};
IEllispe从IDispatch接口派生,这是因为控件容器要使用控件的IDispatch方法访问控件的属性和方法。
注意,所有的标准属性都有bindable和requestedit两个属性,这是因为标准属性的put方法在改变属性之前之后都要向包容器激发变化通知
背景颜色属于标准属性,CStockPropImpl类能够帮助我们。
template < class T, class InterfaceName, const IID* piid = &_ATL_IIDOF(InterfaceName), const GUID* plibid = &CAtlModule::m_libid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
class ATL_NO_VTABLE CStockPropImpl : public IDispatchImpl< InterfaceName, piid, plibid, wMajor, wMinor, tihclass >
{
public:
// Font
HRESULT STDMETHODCALLTYPE put_Font(IFontDisp* pFont)
{
__if_exists(T::m_pFont)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::put_Font/n"));
T* pT = (T*) this;
if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_FONT) == S_FALSE)
return S_FALSE;
pT->m_pFont = 0;
if (pFont)
{
CComQIPtr<IFont, &__uuidof(IFont)> p(pFont);
if (p)
{
CComPtr<IFont> pFont;
p->Clone(&pFont);
if (pFont)
pFont->QueryInterface(__uuidof(IFontDisp), (void**) &pT->m_pFont);
}
}
pT->m_bRequiresSave = TRUE;
if (pT->m_nFreezeEvents == 0)
pT->FireOnChanged(DISPID_FONT);
__if_exists(T::OnFontChanged)
{
pT->OnFontChanged();
}
pT->FireViewChange();
pT->SendOnDataChange(NULL);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE putref_Font(IFontDisp* pFont)
{
__if_exists(T::m_pFont)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::putref_Font/n"));
T* pT = (T*) this;
if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_FONT) == S_FALSE)
return S_FALSE;
pT->m_pFont = pFont;
pT->m_bRequiresSave = TRUE;
if (pT->m_nFreezeEvents == 0)
pT->FireOnChanged(DISPID_FONT);
__if_exists(T::OnFontChanged)
{
pT->OnFontChanged();
}
pT->FireViewChange();
pT->SendOnDataChange(NULL);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE get_Font(IFontDisp** ppFont)
{
__if_exists(T::m_pFont)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::get_Font/n"));
ATLASSERT(ppFont != NULL);
if (ppFont == NULL)
return E_POINTER;
T* pT = (T*) this;
*ppFont = pT->m_pFont;
if (*ppFont != NULL)
(*ppFont)->AddRef();
}
return S_OK;
}
// Picture
HRESULT STDMETHODCALLTYPE put_Picture(IPictureDisp* pPicture)
{
__if_exists(T::m_pPicture)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::put_Picture/n"));
T* pT = (T*) this;
if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_PICTURE) == S_FALSE)
return S_FALSE;
pT->m_pPicture = 0;
if (pPicture)
{
CComQIPtr<IPersistStream, &__uuidof(IPersistStream)> p(pPicture);
if (p)
{
ULARGE_INTEGER l;
p->GetSizeMax(&l);
HGLOBAL hGlob = GlobalAlloc(GHND, l.LowPart);
if (hGlob)
{
CComPtr<IStream> spStream;
CreateStreamOnHGlobal(hGlob, TRUE, &spStream);
if (spStream)
{
if (SUCCEEDED(p->Save(spStream, FALSE)))
{
LARGE_INTEGER l;
l.QuadPart = 0;
spStream->Seek(l, STREAM_SEEK_SET, NULL);
OleLoadPicture(spStream, l.LowPart, FALSE, __uuidof(IPictureDisp), (void**)&pT->m_pPicture);
}
spStream.Release();
}
GlobalFree(hGlob);
}
}
}
pT->m_bRequiresSave = TRUE;
if (pT->m_nFreezeEvents == 0)
pT->FireOnChanged(DISPID_PICTURE);
__if_exists(T::OnPictureChanged)
{
pT->OnPictureChanged();
}
pT->FireViewChange();
pT->SendOnDataChange(NULL);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE putref_Picture(IPictureDisp* pPicture)
{
__if_exists(T::m_pPicture)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::putref_Picture/n"));
T* pT = (T*) this;
if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_PICTURE) == S_FALSE)
return S_FALSE;
pT->m_pPicture = pPicture;
pT->m_bRequiresSave = TRUE;
if (pT->m_nFreezeEvents == 0)
pT->FireOnChanged(DISPID_PICTURE);
__if_exists(T::OnPictureChanged)
{
pT->OnPictureChanged();
}
pT->FireViewChange();
pT->SendOnDataChange(NULL);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE get_Picture(IPictureDisp** ppPicture)
{
__if_exists(T::m_pPicture)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::get_Picture/n"));
ATLASSERT(ppPicture != NULL);
if (ppPicture == NULL)
return E_POINTER;
T* pT = (T*) this;
*ppPicture = pT->m_pPicture;
if (*ppPicture != NULL)
(*ppPicture)->AddRef();
}
return S_OK;
}
// MouseIcon
HRESULT STDMETHODCALLTYPE put_MouseIcon(IPictureDisp* pPicture)
{
__if_exists(T::m_pMouseIcon)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::put_MouseIcon/n"));
T* pT = (T*) this;
if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_MOUSEICON) == S_FALSE)
return S_FALSE;
pT->m_pMouseIcon = 0;
if (pPicture)
{
CComQIPtr<IPersistStream, &__uuidof(IPersistStream)> p(pPicture);
if (p)
{
ULARGE_INTEGER l;
p->GetSizeMax(&l);
HGLOBAL hGlob = GlobalAlloc(GHND, l.LowPart);
if (hGlob)
{
CComPtr<IStream> spStream;
CreateStreamOnHGlobal(hGlob, TRUE, &spStream);
if (spStream)
{
if (SUCCEEDED(p->Save(spStream, FALSE)))
{
LARGE_INTEGER l;
l.QuadPart = 0;
spStream->Seek(l, STREAM_SEEK_SET, NULL);
OleLoadPicture(spStream, l.LowPart, FALSE, __uuidof(IPictureDisp), (void**)&pT->m_pMouseIcon);
}
spStream.Release();
}
GlobalFree(hGlob);
}
}
}
pT->m_bRequiresSave = TRUE;
if (pT->m_nFreezeEvents == 0)
pT->FireOnChanged(DISPID_MOUSEICON);
__if_exists(T::OnMouseIconChanged)
{
pT->OnMouseIconChanged();
}
pT->FireViewChange();
pT->SendOnDataChange(NULL);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE putref_MouseIcon(IPictureDisp* pPicture)
{
__if_exists(T::m_pMouseIcon)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::putref_MouseIcon/n"));
T* pT = (T*) this;
if (pT->m_nFreezeEvents == 0 && pT->FireOnRequestEdit(DISPID_MOUSEICON) == S_FALSE)
return S_FALSE;
pT->m_pMouseIcon = pPicture;
pT->m_bRequiresSave = TRUE;
if (pT->m_nFreezeEvents == 0)
pT->FireOnChanged(DISPID_MOUSEICON);
__if_exists(T::OnMouseIconChanged)
{
pT->OnMouseIconChanged();
}
pT->FireViewChange();
pT->SendOnDataChange(NULL);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE get_MouseIcon(IPictureDisp** ppPicture)
{
__if_exists(T::m_pMouseIcon)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::get_MouseIcon/n"));
ATLASSERT(ppPicture != NULL);
if (ppPicture == NULL)
return E_POINTER;
T* pT = (T*) this;
*ppPicture = pT->m_pMouseIcon;
if (*ppPicture != NULL)
(*ppPicture)->AddRef();
}
return S_OK;
}
IMPLEMENT_STOCKPROP(OLE_COLOR, BackColor, clrBackColor, DISPID_BACKCOLOR)
IMPLEMENT_STOCKPROP(OLE_COLOR, BorderColor, clrBorderColor, DISPID_BORDERCOLOR)
IMPLEMENT_STOCKPROP(OLE_COLOR, FillColor, clrFillColor, DISPID_FILLCOLOR)
IMPLEMENT_STOCKPROP(OLE_COLOR, ForeColor, clrForeColor, DISPID_FORECOLOR)
IMPLEMENT_BOOL_STOCKPROP(AutoSize, bAutoSize, DISPID_AUTOSIZE)
IMPLEMENT_BOOL_STOCKPROP(Valid, bValid, DISPID_VALID)
IMPLEMENT_BOOL_STOCKPROP(Enabled, bEnabled, DISPID_ENABLED)
IMPLEMENT_BOOL_STOCKPROP(TabStop, bTabStop, DISPID_TABSTOP)
IMPLEMENT_BOOL_STOCKPROP(BorderVisible, bBorderVisible, DISPID_BORDERVISIBLE)
IMPLEMENT_BSTR_STOCKPROP(Text, bstrText, DISPID_TEXT)
IMPLEMENT_BSTR_STOCKPROP(Caption, bstrCaption, DISPID_CAPTION)
HRESULT STDMETHODCALLTYPE put_Window(LONG_PTR hWnd)
{
return put_HWND(hWnd);
}
HRESULT STDMETHODCALLTYPE get_Window(LONG_PTR* phWnd)
{
return get_HWND(phWnd);
}
HRESULT STDMETHODCALLTYPE put_HWND(LONG_PTR /*hWnd*/)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::put_HWND/n"));
return E_FAIL;
}
HRESULT STDMETHODCALLTYPE get_HWND(LONG_PTR* phWnd)
{
__if_exists(T::m_hWnd)
{
ATLTRACE(atlTraceControls,2,_T("CStockPropImpl::get_HWND/n"));
ATLASSERT(phWnd != NULL);
if (phWnd == NULL)
return E_POINTER;
T* pT = (T*) this;
*phWnd = reinterpret_cast<LONG_PTR>(pT->m_hWnd);
}
return S_OK;
}
IMPLEMENT_STOCKPROP(LONG, BackStyle, nBackStyle, DISPID_BACKSTYLE)
IMPLEMENT_STOCKPROP(LONG, BorderStyle, nBorderStyle, DISPID_BORDERSTYLE)
IMPLEMENT_STOCKPROP(LONG, BorderWidth, nBorderWidth, DISPID_BORDERWIDTH)
IMPLEMENT_STOCKPROP(LONG, DrawMode, nDrawMode, DISPID_DRAWMODE)
IMPLEMENT_STOCKPROP(LONG, DrawStyle, nDrawStyle, DISPID_DRAWSTYLE)
IMPLEMENT_STOCKPROP(LONG, DrawWidth, nDrawWidth, DISPID_DRAWWIDTH)
IMPLEMENT_STOCKPROP(LONG, FillStyle, nFillStyle, DISPID_FILLSTYLE)
IMPLEMENT_STOCKPROP(SHORT, Appearance, nAppearance, DISPID_APPEARANCE)
IMPLEMENT_STOCKPROP(LONG, MousePointer, nMousePointer, DISPID_MOUSEPOINTER)
IMPLEMENT_STOCKPROP(LONG, ReadyState, nReadyState, DISPID_READYSTATE)
};
我们先来看看模板参数
template < class T, class InterfaceName,
const IID* piid = &_ATL_IIDOF(InterfaceName), const GUID* plibid = &CAtlModule::m_libid, WORD wMajor = 1,
WORD wMinor = 0, class tihclass = CComTypeInfoHolder>
T是控件类的名字;
InterfaceName是接口的名字,该接口拥有标准属性和方法;
piid是接口的GUID的指针;
plibid是类型库GUID的指针;
wMajor是主版本号;
wMinor是次版本号;
实际运用中,只需要指定前面两个参数,其余皆可以采用默认值。
CStockPropImpl类为我们实现了所有标准属性的访问方法,但是我们只需要实现两个属性,因此我们只为我们需要的标准属性提供数据成员,成员变量的名称要与CStockPropImpl类使用的名字一致。如若不信,我们看看下面的宏
IMPLEMENT_STOCKPROP(OLE_COLOR, BackColor, clrBackColor, DISPID_BACKCOLOR)
该宏的定义如下:
#define IMPLEMENT_STOCKPROP(type, fname, pname, dispid) /
HRESULT STDMETHODCALLTYPE put_##fname(type pname) /
{ /
__if_exists(T::m_##pname) /
。。。。。。。。
所以CStockPropImpl类假定控件类T拥有m_clrBackColor成员变量
这很麻烦,但是不必烦恼,因为向导已经为我们定义了合适的成员变量。但是我们还是需要知道这些,万一我们不小心删除了代码,我们知道该如何恢复。
注意IMPLEMENT_STOCKPROP宏只能用于CStockPropImpl类
CEllispe类里边的属性声明为:
OLE_COLOR m_clrBackColor;
void OnBackColorChanged()
{
ATLTRACE(_T("OnBackColorChanged/n"));
}
OnBackColorChanged方法会被CStockPropImpl类在属性值修改的时候调用,如果我们需要对属性的修改进行即时反映,可以在该函数里面填写代码
其他属性也是如此。
现在我们的控件Ellipse已经拥有了背景颜色、背景状态两种标准属性,在Activex测试容器中右键点击控件的边缘,弹出菜单,选择属性,可以看到
我们可以设置背景颜色了。试着用用其他的功能,我们可以设置控件的环境属性,还可以把控件保存到流和结构化存储文件中。但是目前不支持属性包方式保存。
我撰写本文的时候,正是Windows2000已经普及的时候,出于效率的考虑和开发程序减少从ANSI和UNICODE转换的额外负担,我在本文示例程序中全部使用Unicode风格编码,工程中设置如下:
到这里忍不住提高点难度,用GDI绘制笛卡尔坐标系很简单,用OpenGL绘制三维笛卡尔坐标系才棒。如果读者不想涉及太多的OpenGL技巧,可以掠过这一节,用GDI绘图。
在stdafx.h文件中增加以下代码:
#include <gl/gl.h>
#include <gl/glu.h>
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "glaux.lib")
#pragma warning(disable : 4100)
#include <gl/glaux.h>
#pragma comment(lib, "winmm.lib")
为我们的控件添加成员函数
//设置屏幕DC的像素格式,OpenGL绘图环境初始化
BOOL CEllispe::bSetupPixelFormat(HDC hdc)
{
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
int pixelformat;
if ( (pixelformat = ChoosePixelFormat(hdc, &pfd)) == 0 )
{
ATLASSERT(FALSE);
return FALSE;
}
if (SetPixelFormat(hdc, pixelformat, &pfd) == FALSE)
{
ATLASSERT(FALSE);
return FALSE;
}
return TRUE;
}
//初始化OpenGL绘制的环境
void CEllispe::CreateContext(HDC hdc, RECT& rc)
{
PIXELFORMATDESCRIPTOR pfd;
if (!bSetupPixelFormat(hdc))
return;
int n = ::GetPixelFormat(hdc);
::DescribePixelFormat(hdc, n, sizeof(pfd), &pfd);
m_hrc = wglCreateContext(hdc);
wglMakeCurrent(hdc, m_hrc);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
//绘制x、y、z坐标轴
void CEllispe::RenderScene(void)
{
glClearColor(1.0f, 1.0f, 1.0f, 10.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glColor3f(1.0f,1.0f,1.0f);
//绕x轴逆时针旋转15度
glRotatef(15.0f,1.0f,0.0f,0.0f);
//绕y轴顺时针旋转angle度
glRotatef(-30,0.0f,1.0f,0.0f);
glBegin(GL_LINES);
//绘制X轴
glColor3f(1.0f,0.0f,0.0f);
glVertex3f(-100.0f,0.0f,0.0f);
glVertex3f(100.0f,0.0f,0.0f);
glVertex3f(100.0f,0.0f,0.0f);
glVertex3f(95.0f,0.0f,5.0f);
glVertex3f(100.0f,0.0f,0.0f);
glVertex3f(95.0f,0.0f,-5.0f);
//绘制Y轴
glColor3f(0.0f,1.0f,0.0f);
glVertex3f(0.0f,-100.0f,0.0f);
glVertex3f(0.0f,100.0f,0.0f);
glVertex3f(0.0f,100.0f,0.0f);
glVertex3f(-5.0f,95.0f,0.0f);
glVertex3f(0.0f,100.0f,0.0f);
glVertex3f(5.0f,95.0f,0.0f);
//绘制Z轴
glColor3f(0.0f,0.0f,1.0f);
glVertex3f(0.0f,0.0f,-100.0f);
glVertex3f(0.0f,0.0f,100.0f);
glVertex3f(0.0f,0.0f,100.0f);
glVertex3f(5.0f,0.0f,95.0f);
glVertex3f(0.0f,0.0f,100.0f);
glVertex3f(-5.0f,0.0f,95.0f);
glEnd();
glPopMatrix();
glFinish();
SwapBuffers(wglGetCurrentDC());
}
在OnCreate里面添加如下代码:
LRESULT CEllispe::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
HDC hdc = GetDC();
RECT rc;
GetClientRect(&rc);
CreateContext(hdc, rc);
return 0;
}
在OnDestroy里面添加如下代码:
LRESULT CEllispe::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
::wglMakeCurrent(NULL, NULL);
if (m_hrc)
{
::wglDeleteContext(m_hrc);
m_hrc = NULL;
}
return 0;
}
在OnDraw里面添加如下代码:
HRESULT CEllispe::OnDraw(ATL_DRAWINFO& di)
{
HDC hdc = di.hdcDraw;
wglMakeCurrent(hdc, m_hrc);
RenderScene();
return __super::OnDraw(di);
}
最后一定要在CEllipse头文件中加上宏
BEGIN_MSG_MAP(CEllispe)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
CHAIN_MSG_MAP(CComCompositeControl<CEllispe>)
END_MSG_MAP()
效果如图:
另外发现一个问题glut库函数在ATL7.1中使用会导致工程生成的控件注册失败。
我要为我的控件添加自己的属性,XColor,用来保存X轴的颜色。右键点击IEllipse接口,选择添加属性,
向导在idl文件中添加如下代码:
[propget, id(1), helpstring("属性 XColor"), bindable, requestedit] HRESULT XColor([out, retval] OLE_COLOR* pVal);
[propput, id(1), helpstring("属性 XColor"), bindable, requestedit] HRESULT XColor([in] OLE_COLOR newVal);
修改idl文件中的代码:
const int DISPID_XCOLOR=1;
[propget, id(DISPID_XCOLOR), helpstring("属性 XColor"), bindable, requestedit] HRESULT XColor([out, retval] OLE_COLOR* pVal);
[propput, id(DISPID_XCOLOR), helpstring("属性 XColor"), bindable, requestedit] HRESULT XColor([in] OLE_COLOR newVal);
向导在.h文件中增加如下代码:
STDMETHOD(get_XColor)(OLE_COLOR* pVal);
STDMETHOD(put_XColor)(OLE_COLOR newVal);
向导在.cpp文件中增加如下代码:
STDMETHODIMP CEllispe::get_XColor(OLE_COLOR* pVal)
{
// TODO: 在此添加实现代码
return S_OK;
}
STDMETHODIMP CEllispe::put_XColor(OLE_COLOR newVal)
{
// TODO: 在此添加实现代码
return S_OK;
}
现在我们来实现添加自己的代码实现属性:
第一步,先添加私有成员变量 OLE_COLOR m_XColor;
第二步,在get和put函数内部填写代码
STDMETHODIMP CEllispe::get_XColor(OLE_COLOR* pVal)
{
if(pVal==NULL)
return E_POINTER;
*pVal=m_XColor;
return S_OK;
}
STDMETHODIMP CEllispe::put_XColor(OLE_COLOR newVal)
{
if(m_XColor==newVal)
return S_OK;
if(!m_nFreezeEvents)//m_nFreezeEvents是从父类CComCompositeControl继承下来的,只有为0时才可以设置属性
{
if(FireOnRequestEdit(DISPID_XCOLOR)==S_FALSE)//通知容器控件准备改变属性,容器许可将返回S_OK
return S_FALSE;
}
//////////////////////////////
//真正本属性需要的代码就此一行
m_XColor=newVal;
///////////////////////////////
m_bRequiresSave=TRUE;//m_bRequiresSave从CComControlBase类继承,为TRUE表示属性已被修改,需要保存
if(!m_nFreezeEvents)
{
FireOnChanged(DISPID_XCOLOR);//通知容器属性值已经修改
}
FireViewChange();//通知容器控件窗口需要重绘,用于绘制图形的地方,不是所有场合都必须
SendOnDataChange(NULL);//通知所有建立连接的通报接收器控件的属性已经改变
return S_OK;
}
真得非常简单,如你所看到的,其实COM对象的属性实质是一对方法,只是VB语言让你以为“=”运算符就可以获取/设置属性值,内部还是调用了两个方法
有了自定义属性的基础,编写自定义方法非常简单,在这里我就不再赘述了,可以参考我以前的文章<<ATL7.1创建房屋对象>>。
唯一的区别是不需要考虑通知容器。
ActiveX控件有标准方法。遗憾的是我还没有找到ATL7.1中通过向导自动生成他们代码的方法,或许根本就没有。下面列出的是(可能是)ATL7.1支持的ActiveX控件的所有的标准方法ID。
#define DISPID_REFRESH (-550)
#define DISPID_DOCLICK (-551)
#define DISPID_ABOUTBOX (-552)
#define DISPID_ADDITEM (-553)
#define DISPID_CLEAR (-554)
#define DISPID_REMOVEITEM (-555)
#define DISPID_CLICK (-600)
#define DISPID_DBLCLICK (-601)
#define DISPID_KEYDOWN (-602)
#define DISPID_KEYPRESS (-603)
#define DISPID_KEYUP (-604)
#define DISPID_MOUSEDOWN (-605)
#define DISPID_MOUSEMOVE (-606)
#define DISPID_MOUSEUP (-607)
#define DISPID_ERROREVENT (-608)
#define DISPID_READYSTATECHANGE (-609)
#define DISPID_CLICK_VALUE (-610)
#define DISPID_RIGHTTOLEFT (-611)
#define DISPID_TOPTOBOTTOM (-612)
#define DISPID_THIS (-613)
现在我们手动实现其中的一个AboutBox方法,该方法弹出AboutBox对话框。我们可以用它显示我们的一些信息。
首先在idl文件中加上如下代码:
[id(DISPID_ABOUTBOX)] HRESULT AboutBox();
然后在.h中声明函数原型,然后在.cpp文件中
STDMETHODIMP CEllispe::AboutBox()
{
MessageBox(L"这是我们的ATL编写ActivX控件的教程,属于飞天公司内部资料");
return S_OK;
}
好了,标准方法也添加完毕。
注意,我们的控件是复合控件,它包含了一个对话框资源IDD_ELLISPE,如果我们在该对话框资源上加上控件,程序运行时会自动看见,你可以把我们的控件想象成对话框程序。
我们这里并不想在该对话框上添加任何东西。如果我们想弹出对话框,请添加新的对话框资源。ATL中使用对话框涉及到其他知识,这里不涉及。
`ATL7.1似乎不支持标准事件
本文章为转载内容,我们尊重原作者对文章享有的著作权。如有内容错误或侵权问题,欢迎原作者联系我们进行内容更正或删除文章。
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
Qt自定义控件学习扩展QPushButton
Qt自定义控件学习扩展QPushButton
QPushButton -
VC++编写ActiveX控件
前言: 暑假在做一个项目的时候,本来是用C#.NET来写的一个港口进出闸的
控件 数据类型 Visual