关于ActiveX Control开发总结 MFC篇
2015-05-19 15:43
204 查看
为了方便的移植及重用自己编写的控件,这时候我们就要用到ActiveX控件技术来封装自己的控件类。
封装一个ActiveX控件需要考虑:
1、提供用户设置的属性。
2、提供用户使用的接口函数。
3、控件事件的通知。
4、控件响应用户的操作。
5、控件的绘制。
6、异常处理。
添加用户设置属性方法:
打开类视图展开XXlib选择控件接口右键菜单添加-〉添加属性打开属性添加向导,设置属性后完成。
在idl文件自动生成代码:
[cpp] view
plaincopy
library ScribbleLib
{
importlib(STDOLE_TLB);
// Primary dispatch interface for CScribbleCtrl
[ uuid(9B217CBB-3D90-444A-898B-271B9EF1B36A),
helpstring("Dispatch interface for Scribble Control")]
dispinterface _DScribble
{
properties:
[id(1) , helpstring("画笔颜色")] OLE_COLOR PenColor;
methods:
};
如果需要怎加自定义的枚举属性可以在之前定义
[cpp] view
plaincopy
#include <olectl.h>
#include <idispids.h>
typedef enum { Blank, SmallGrid, MediumGrid, LargeGrid } GridType;
[
uuid(0A33EFF3-46C9-4B38-B2DC-05C42C99D093), version(1.0),
helpfile("Scribble.hlp"),
helpstring("Scribble ActiveX Control module"),
control
]
............
属性这样声明:
[cpp] view
plaincopy
properties:
[id(1) , helpstring("画笔颜色")] OLE_COLOR PenColor;
[id(2) , helpstring("显示网格")] GridType ShowGird;
...........
在控件头文件中生成代码
[cpp] view
plaincopy
class CScribbleCtrl : public COleControl
{
DECLARE_DYNCREATE(CScribbleCtrl)
// Constructor
public:
CScribbleCtrl();
// Implementation
protected:
~CScribbleCtrl();
.......................
// Dispatch and event IDs
public:
enum {
dispidShowGird = 2,
dispidPenColor = 1
};
protected:
// 根据向导的选择如果选择get/put则生成get/put函数,选择变量形式则为当前代码
OLE_COLOR m_PenColor;
void OnPenColorChanged(void);
USHORT m_ShowGird;
void OnShowGirdChanged(void);
.................................
};
在控件cpp文件中生成代码
[cpp] view
plaincopy
// Dispatch map
BEGIN_DISPATCH_MAP(CScribbleCtrl, COleControl)
DISP_PROPERTY_NOTIFY_ID(CScribbleCtrl, "ShowGird", dispidShowGird, m_ShowGird, OnShowGirdChanged, VT_UI2)
DISP_PROPERTY_NOTIFY_ID(CScribbleCtrl, "PenColor", dispidPenColor, m_PenColor, OnPenColorChanged, VT_COLOR)
DISP_PROPERTY_NOTIFY_ID(CScribbleCtrl, "DrawPen", dispiddrawPen, m_DrawPen, OndrawPenChanged, VT_UI2)
END_DISPATCH_MAP()
// CScribbleCtrl::DoPropExchange - Persistence support
void CScribbleCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// 绑定控件属性默认值,显示在控件属性页中
PX_UShort(pPX, _T("ShowGird"), m_ShowGird, Blank);
PX_Color(pPX, _T("PenColor"), m_PenColor, RGB(0, 0, 0));
if (pPX->IsLoading())
{
// 加载用户在控件属性页中设置的控件属性值
}
}
// CScribbleCtrl message handlers
void CScribbleCtrl::OnShowGirdChanged(void)
{
Refresh(); // 刷新控件
}
void CScribbleCtrl::OnPenColorChanged(void)
{
}
添加用户使用的接口函数:
与添加属性的方法一样,在添加菜单选择添加方法。
[cpp] view
plaincopy
// idl 代码段
dispinterface _DScribble
{
properties:
......................
methods:
[id(3), helpstring("method GetScribbleBmp")] ULONG GetScribbleHBITMAP(void);
.......................
//头文件代码段
.......................
// Dispatch and event IDs
public:
enum {
dispidGetScribbleBmp = 4L,
dispidPenColor = 2,
dispidShowGird = 1
};
........................
protected:
ULONG GetScribbleHBITMAP(void);
.......................
//cpp文件代码段
........................
// Dispatch map
BEGIN_DISPATCH_MAP(CScribbleCtrl, COleControl)
.....................
DISP_FUNCTION_ID(CScribbleCtrl, "GetScribbleHBITMAP", dispidGetScribbleBmp, GetScribbleHBITMAP, VT_UI4, VTS_NONE)
END_DISPATCH_MAP()
ULONG CScribbleCtrl::GetScribbleHBITMAP(void)
{
.............
}
.............
添加控件事件的通知
打开类视图展开XXlib选择控件事件接口(一般为_DXXXXEvents)右键菜单添加-〉添加方法打开方法添加向导,设置后完成。
COleControl 已经封装了一些内置的事件
事件映射宏如下:
[cpp] view
plaincopy
EVENT_STOCK_CLICK()
EVENT_STOCK_DBLCLICK()
EVENT_STOCK_KEYDOWN()
EVENT_STOCK_KEYPRESS()
EVENT_STOCK_KEYUP()
EVENT_STOCK_MOUSEDOWN()
EVENT_STOCK_MOUSEMOVE()
EVENT_STOCK_MOUSEUP()
EVENT_STOCK_ERROREVENT()
EVENT_STOCK_READYSTATECHANGE()
函数原型
[cpp] view
plaincopy
// Firing functions for stock events
void FireKeyDown(USHORT* pnChar, short nShiftState);
void FireKeyUp(USHORT* pnChar, short nShiftState);
void FireKeyPress(USHORT* pnChar);
void FireMouseDown(short nButton, short nShiftState,
OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
void FireMouseUp(short nButton, short nShiftState,
OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
void FireMouseMove(short nButton, short nShiftState,
OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
void FireClick();
void FireDblClick();
void FireError(SCODE scode, LPCTSTR lpszDescription, UINT nHelpID = 0);
void FireReadyStateChange();
添加自定义的事件通知使用向导后需要编写一些代码,看代码段
[cpp] view
plaincopy
//idl代码段
// Event dispatch interface for CScribbleCtrl
[ uuid(DA9841E5-98B9-4674-939E-8EC6BEE35273),
helpstring("Event interface for Scribble Control") ]
dispinterface _DScribbleEvents
{
properties:
// Event interface has no properties
methods:
[id(1), helpstring("method ScribbleChange")] void ScribbleChange(VARIANT_BOOL bIsEmpty);
};
//头文件
//同样会自动生成一个id
// Dispatch and event IDs
public:
enum {
dispidScribbleChange = 1L,
..............
}
........
void ScribbleChange(VARIANT_BOOL bIsEmpty);
........
//cpp
// 会自动在函数影射中增加这一条,注释掉没有用
// Dispatch map
BEGIN_DISPATCH_MAP(CScribbleCtrl, COleControl)
//DISP_FUNCTION_ID(CScribbleCtrl, "ScribbleChange", dispidScribbleChange, ScribbleChange, VT_EMPTY, VTS_BOOL)
END_DISPATCH_MAP()
//需要手工在事件映射内添加代码,要不然使用控件的地方得不到事件通知
// Event map
BEGIN_EVENT_MAP(CScribbleCtrl, COleControl)
EVENT_CUSTOM_ID("ScribbleChange", dispidScribbleChange, ScribbleChange, VTS_BOOL)
END_EVENT_MAP()
// 时间函数实现
void CScribbleCtrl::ScribbleChange(VARIANT_BOOL bIsEmpty)
{
FireEvent(dispidScribbleChange, EVENT_PARAM(VTS_BOOL), bIsEmpty);
}
//如果控件触发了这个事件需要对控件的容器进行事件通知可以直接调用
{
.........
ScribbleChange(FALSE);
}
控件响应用户的操作:
一般来说需要响应的是
WM_MOUSEMOVE;WM_LBUTTONDOWN;WM_LBUTTONUP;WM_MOUSELEAVE;WM_RBUTTONDOWN
这里只补充一下关于WM_MOUSELEAVE响应的方法;
首先在MOUSEMOVE函数中使用如下方法让系统通知WM_MOUSELEAVE
[cpp] view
plaincopy
if (!m_bSetTrack)
{
TRACKMOUSEEVENT trackm;
ZeroMemory(&trackm, sizeof TRACKMOUSEEVENT);
trackm.cbSize = sizeof TRACKMOUSEEVENT;
trackm.dwFlags = TME_HOVER | TME_LEAVE;
trackm.hwndTrack = m_hWnd;
m_bSetTrack = TrackMouseEvent(&trackm);
}
然后在响应WM_MOUSELEAVE消息后把m_bSetTrack变量重置一下即可
控件的绘制:
void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
rcBounds为控件绘制大小其实就是控件客户区域,rcInvalid为更新绘制区域
一般可以使用包装的双缓冲类进行绘制
包装类如下
[cpp] view
plaincopy
#ifndef _MEMDC_H_
#define _MEMDC_H_
// WTL CMemeryDC for MFC use
namespace MFC
{
class CMemDC : public CDC
{
public:
// Data members
CDC * m_pDCOriginal;
RECT m_rcPaint;
CBitmap m_bmp;
CBitmap* m_pBmpOld;
// Constructor/destructor
CMemDC(CDC *pDC, const RECT& rcPaint) : m_pDCOriginal(pDC), m_pBmpOld(NULL)
{
m_rcPaint = rcPaint;
CreateCompatibleDC(m_pDCOriginal);
ASSERT(m_hDC != NULL);
m_bmp .CreateCompatibleBitmap(m_pDCOriginal, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top);
ASSERT(m_bmp.m_hObject != NULL);
m_pBmpOld = SelectObject(&m_bmp);
SetViewportOrg(-m_rcPaint.left, -m_rcPaint.top);
}
~CMemDC()
{
m_pDCOriginal->BitBlt(m_rcPaint.left, m_rcPaint.top, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top, this, m_rcPaint.left, m_rcPaint.top, SRCCOPY);
SelectObject(m_pBmpOld);
}
};
}
#endif
调用例子
[cpp] view
plaincopy
void CScribbleCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
MFC::CMemDC dcMem(pdc, rcInvalid);
dcMem.FillSolidRect(rcBounds, RGB(255,255,255));
// 绘制控件
.......
}
异常处理:
控件异常处理一般来说我基本上没有考虑,但是这个肯定是需要的
封装一个ActiveX控件需要考虑:
1、提供用户设置的属性。
2、提供用户使用的接口函数。
3、控件事件的通知。
4、控件响应用户的操作。
5、控件的绘制。
6、异常处理。
添加用户设置属性方法:
打开类视图展开XXlib选择控件接口右键菜单添加-〉添加属性打开属性添加向导,设置属性后完成。
在idl文件自动生成代码:
[cpp] view
plaincopy
library ScribbleLib
{
importlib(STDOLE_TLB);
// Primary dispatch interface for CScribbleCtrl
[ uuid(9B217CBB-3D90-444A-898B-271B9EF1B36A),
helpstring("Dispatch interface for Scribble Control")]
dispinterface _DScribble
{
properties:
[id(1) , helpstring("画笔颜色")] OLE_COLOR PenColor;
methods:
};
如果需要怎加自定义的枚举属性可以在之前定义
[cpp] view
plaincopy
#include <olectl.h>
#include <idispids.h>
typedef enum { Blank, SmallGrid, MediumGrid, LargeGrid } GridType;
[
uuid(0A33EFF3-46C9-4B38-B2DC-05C42C99D093), version(1.0),
helpfile("Scribble.hlp"),
helpstring("Scribble ActiveX Control module"),
control
]
............
属性这样声明:
[cpp] view
plaincopy
properties:
[id(1) , helpstring("画笔颜色")] OLE_COLOR PenColor;
[id(2) , helpstring("显示网格")] GridType ShowGird;
...........
在控件头文件中生成代码
[cpp] view
plaincopy
class CScribbleCtrl : public COleControl
{
DECLARE_DYNCREATE(CScribbleCtrl)
// Constructor
public:
CScribbleCtrl();
// Implementation
protected:
~CScribbleCtrl();
.......................
// Dispatch and event IDs
public:
enum {
dispidShowGird = 2,
dispidPenColor = 1
};
protected:
// 根据向导的选择如果选择get/put则生成get/put函数,选择变量形式则为当前代码
OLE_COLOR m_PenColor;
void OnPenColorChanged(void);
USHORT m_ShowGird;
void OnShowGirdChanged(void);
.................................
};
在控件cpp文件中生成代码
[cpp] view
plaincopy
// Dispatch map
BEGIN_DISPATCH_MAP(CScribbleCtrl, COleControl)
DISP_PROPERTY_NOTIFY_ID(CScribbleCtrl, "ShowGird", dispidShowGird, m_ShowGird, OnShowGirdChanged, VT_UI2)
DISP_PROPERTY_NOTIFY_ID(CScribbleCtrl, "PenColor", dispidPenColor, m_PenColor, OnPenColorChanged, VT_COLOR)
DISP_PROPERTY_NOTIFY_ID(CScribbleCtrl, "DrawPen", dispiddrawPen, m_DrawPen, OndrawPenChanged, VT_UI2)
END_DISPATCH_MAP()
// CScribbleCtrl::DoPropExchange - Persistence support
void CScribbleCtrl::DoPropExchange(CPropExchange* pPX)
{
ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
COleControl::DoPropExchange(pPX);
// 绑定控件属性默认值,显示在控件属性页中
PX_UShort(pPX, _T("ShowGird"), m_ShowGird, Blank);
PX_Color(pPX, _T("PenColor"), m_PenColor, RGB(0, 0, 0));
if (pPX->IsLoading())
{
// 加载用户在控件属性页中设置的控件属性值
}
}
// CScribbleCtrl message handlers
void CScribbleCtrl::OnShowGirdChanged(void)
{
Refresh(); // 刷新控件
}
void CScribbleCtrl::OnPenColorChanged(void)
{
}
添加用户使用的接口函数:
与添加属性的方法一样,在添加菜单选择添加方法。
[cpp] view
plaincopy
// idl 代码段
dispinterface _DScribble
{
properties:
......................
methods:
[id(3), helpstring("method GetScribbleBmp")] ULONG GetScribbleHBITMAP(void);
.......................
//头文件代码段
.......................
// Dispatch and event IDs
public:
enum {
dispidGetScribbleBmp = 4L,
dispidPenColor = 2,
dispidShowGird = 1
};
........................
protected:
ULONG GetScribbleHBITMAP(void);
.......................
//cpp文件代码段
........................
// Dispatch map
BEGIN_DISPATCH_MAP(CScribbleCtrl, COleControl)
.....................
DISP_FUNCTION_ID(CScribbleCtrl, "GetScribbleHBITMAP", dispidGetScribbleBmp, GetScribbleHBITMAP, VT_UI4, VTS_NONE)
END_DISPATCH_MAP()
ULONG CScribbleCtrl::GetScribbleHBITMAP(void)
{
.............
}
.............
添加控件事件的通知
打开类视图展开XXlib选择控件事件接口(一般为_DXXXXEvents)右键菜单添加-〉添加方法打开方法添加向导,设置后完成。
COleControl 已经封装了一些内置的事件
事件映射宏如下:
[cpp] view
plaincopy
EVENT_STOCK_CLICK()
EVENT_STOCK_DBLCLICK()
EVENT_STOCK_KEYDOWN()
EVENT_STOCK_KEYPRESS()
EVENT_STOCK_KEYUP()
EVENT_STOCK_MOUSEDOWN()
EVENT_STOCK_MOUSEMOVE()
EVENT_STOCK_MOUSEUP()
EVENT_STOCK_ERROREVENT()
EVENT_STOCK_READYSTATECHANGE()
函数原型
[cpp] view
plaincopy
// Firing functions for stock events
void FireKeyDown(USHORT* pnChar, short nShiftState);
void FireKeyUp(USHORT* pnChar, short nShiftState);
void FireKeyPress(USHORT* pnChar);
void FireMouseDown(short nButton, short nShiftState,
OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
void FireMouseUp(short nButton, short nShiftState,
OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
void FireMouseMove(short nButton, short nShiftState,
OLE_XPOS_PIXELS x, OLE_YPOS_PIXELS y);
void FireClick();
void FireDblClick();
void FireError(SCODE scode, LPCTSTR lpszDescription, UINT nHelpID = 0);
void FireReadyStateChange();
添加自定义的事件通知使用向导后需要编写一些代码,看代码段
[cpp] view
plaincopy
//idl代码段
// Event dispatch interface for CScribbleCtrl
[ uuid(DA9841E5-98B9-4674-939E-8EC6BEE35273),
helpstring("Event interface for Scribble Control") ]
dispinterface _DScribbleEvents
{
properties:
// Event interface has no properties
methods:
[id(1), helpstring("method ScribbleChange")] void ScribbleChange(VARIANT_BOOL bIsEmpty);
};
//头文件
//同样会自动生成一个id
// Dispatch and event IDs
public:
enum {
dispidScribbleChange = 1L,
..............
}
........
void ScribbleChange(VARIANT_BOOL bIsEmpty);
........
//cpp
// 会自动在函数影射中增加这一条,注释掉没有用
// Dispatch map
BEGIN_DISPATCH_MAP(CScribbleCtrl, COleControl)
//DISP_FUNCTION_ID(CScribbleCtrl, "ScribbleChange", dispidScribbleChange, ScribbleChange, VT_EMPTY, VTS_BOOL)
END_DISPATCH_MAP()
//需要手工在事件映射内添加代码,要不然使用控件的地方得不到事件通知
// Event map
BEGIN_EVENT_MAP(CScribbleCtrl, COleControl)
EVENT_CUSTOM_ID("ScribbleChange", dispidScribbleChange, ScribbleChange, VTS_BOOL)
END_EVENT_MAP()
// 时间函数实现
void CScribbleCtrl::ScribbleChange(VARIANT_BOOL bIsEmpty)
{
FireEvent(dispidScribbleChange, EVENT_PARAM(VTS_BOOL), bIsEmpty);
}
//如果控件触发了这个事件需要对控件的容器进行事件通知可以直接调用
{
.........
ScribbleChange(FALSE);
}
控件响应用户的操作:
一般来说需要响应的是
WM_MOUSEMOVE;WM_LBUTTONDOWN;WM_LBUTTONUP;WM_MOUSELEAVE;WM_RBUTTONDOWN
这里只补充一下关于WM_MOUSELEAVE响应的方法;
首先在MOUSEMOVE函数中使用如下方法让系统通知WM_MOUSELEAVE
[cpp] view
plaincopy
if (!m_bSetTrack)
{
TRACKMOUSEEVENT trackm;
ZeroMemory(&trackm, sizeof TRACKMOUSEEVENT);
trackm.cbSize = sizeof TRACKMOUSEEVENT;
trackm.dwFlags = TME_HOVER | TME_LEAVE;
trackm.hwndTrack = m_hWnd;
m_bSetTrack = TrackMouseEvent(&trackm);
}
然后在响应WM_MOUSELEAVE消息后把m_bSetTrack变量重置一下即可
控件的绘制:
void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
rcBounds为控件绘制大小其实就是控件客户区域,rcInvalid为更新绘制区域
一般可以使用包装的双缓冲类进行绘制
包装类如下
[cpp] view
plaincopy
#ifndef _MEMDC_H_
#define _MEMDC_H_
// WTL CMemeryDC for MFC use
namespace MFC
{
class CMemDC : public CDC
{
public:
// Data members
CDC * m_pDCOriginal;
RECT m_rcPaint;
CBitmap m_bmp;
CBitmap* m_pBmpOld;
// Constructor/destructor
CMemDC(CDC *pDC, const RECT& rcPaint) : m_pDCOriginal(pDC), m_pBmpOld(NULL)
{
m_rcPaint = rcPaint;
CreateCompatibleDC(m_pDCOriginal);
ASSERT(m_hDC != NULL);
m_bmp .CreateCompatibleBitmap(m_pDCOriginal, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top);
ASSERT(m_bmp.m_hObject != NULL);
m_pBmpOld = SelectObject(&m_bmp);
SetViewportOrg(-m_rcPaint.left, -m_rcPaint.top);
}
~CMemDC()
{
m_pDCOriginal->BitBlt(m_rcPaint.left, m_rcPaint.top, m_rcPaint.right - m_rcPaint.left, m_rcPaint.bottom - m_rcPaint.top, this, m_rcPaint.left, m_rcPaint.top, SRCCOPY);
SelectObject(m_pBmpOld);
}
};
}
#endif
调用例子
[cpp] view
plaincopy
void CScribbleCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
if (!pdc)
return;
// TODO: Replace the following code with your own drawing code.
MFC::CMemDC dcMem(pdc, rcInvalid);
dcMem.FillSolidRect(rcBounds, RGB(255,255,255));
// 绘制控件
.......
}
异常处理:
控件异常处理一般来说我基本上没有考虑,但是这个肯定是需要的
相关文章推荐
- 关于ActiveX Control开发总结 MFC篇
- 关于js开发的总结
- ios SDK开发之关于CoreAnimation的一些注意点总结
- 关于蓝牙4.0 BLE开发坑总结
- IOS开发(31)之关于self.用法的一些总结(转载)
- iOS开发中关于UIImage的知识点总结
- Android关于网络访问app应用开发相关的异常总结
- 关于fan客•尚汇项目开发的个人总结
- 关于KS系列Android开发学习总结
- 关于移动端页面开发(微信内置浏览器)总结
- 关于敏捷开发的一点总结与感悟
- 关于php开发的一些总结
- 关于ionic开发中遇到的坑与总结
- 关于Android开发的一些个人总结(2016.04)
- android开发中关于写入IMEI号问题总结
- 关于网站开发文件编码的一点总结[转]
- android 源码开发 关于编译等小知识点总结
- 关于Talend的Patch分支对应Eclipse开发环境的配置总结.
- javaweb开发中关于字符编码出现乱码问题的总结
- ios SDK开发之关于CoreAnimation的一些注意点总结