一 创建ATL项目
我们创建dll形式的ATL项目EllipseCase 如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/39544f5bacae844882f20b2dc3363390.png)
二 创建ActiveX控件
![](https://i-blog.csdnimg.cn/blog_migrate/1b868c5fa4e347f3f5b755c380e4c2ae.png)
我打算创建一个Ellipse控件,该控件拥有背景属性,可以改变背景颜色。
该控件绘制一个笛卡尔坐标系,用户可以通过右键菜单修改X、Y、Z轴的颜色。
好啦,现就这么多了,让我上路吧。
![](https://i-blog.csdnimg.cn/blog_migrate/7533e473c1869524e6298ecafa89b643.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3357fecdc2be8fe1eca471ed18d5e1c8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f10830dfdda31f609ac7b993a4849926.png)
重要提醒:如果你要创建一个能够处理
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测试容器中右键点击控件的边缘,弹出菜单,选择属性,可以看到
![](https://i-blog.csdnimg.cn/blog_migrate/b56952f507da3ac8e78f51c4915328f1.png)
我们可以设置背景颜色了。试着用用其他的功能,我们可以设置控件的环境属性,还可以把控件保存到流和结构化存储文件中。但是目前不支持属性包方式保存。
我撰写本文的时候,正是Windows2000已经普及的时候,出于效率的考虑和开发程序减少从ANSI和UNICODE转换的额外负担,我在本文示例程序中全部使用Unicode风格编码,工程中设置如下:
![](https://i-blog.csdnimg.cn/blog_migrate/f4195111d0506ecf8d6cc4b9d67bf285.png)
到这里忍不住提高点难度,用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()
效果如图:
![](https://i-blog.csdnimg.cn/blog_migrate/b3e7314e3b58681981e99db8ec571040.png)
另外发现一个问题glut库函数在ATL7.1中使用会导致工程生成的控件注册失败。
我要为我的控件添加自己的属性,XColor,用来保存X轴的颜色。右键点击IEllipse接口,选择添加属性,
![](https://i-blog.csdnimg.cn/blog_migrate/fef5bdd8c4b98b048c3cabeda463be42.png)
![](https://i-blog.csdnimg.cn/blog_migrate/97c3ba81b9d7cdd8b0adedcbad25a944.png)
向导在
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)
{