一直想写一些ATL、ActiveX的东西,但是一直都没有下定决心去写,一来是自己对这方面的东西不太了解,写不出什么名堂;二来就是懒,懒得思考,懒得动手。这篇文章只是记录一下ATL ActiveX控件的一个大概写法跟使用方法,方便有这方面需求的同学快速上手。
一、ATL ActiveX控件编写
我做的是一个简单的图表控件,提供扇形图跟柱状图两种表现方式。
我们先简单列一下图表的成员属性:
1、图表样式:柱状图还是扇形图。
typedef enum _em_chart_type
{
ctNull,
ctHistogram,
ctPieChart
} ChartType;
ChartType m_chartType;
2、图表中每一个项目的信息:名称、百分比和显示的颜色。
typedef struct _st_item_info
{
TCHAR name[64];
FLOAT percent;
OLE_COLOR color;
} ItemInfo;
std::list<ItemInfo><span style="white-space:pre"> </span>m_items;
3、图表是否显示:
VARIANT_BOOL m_bShow;
好了,只是一个简单的例子,就这么几个。
第二步我们来看看ActiveX控件创建的步骤:
1、新建一个ATL项目,确定。
2、添加一个ATL控件类,给控件一个名字,确定。
3、给我们的控件添加必要的属性设置。
点击完成后,我们可以看到VS自动给我们添加两个导出函数:
STDMETHOD(get_chartType)(SHORT* pVal);
STDMETHOD(put_chartType)(SHORT newVal);
4、给我们的控件添加一些必须的接口:
注意:ATL中函数的返回值添加,我们必须先选一个指针类型的参数,此时才能选择该参数是否作为输出参数还是返回值。
我添加的方法一共有下面几个:
STDMETHOD(Show)(VARIANT_BOOL bShow);
STDMETHOD(AddItem)(BSTR itemName, FLOAT itemPersent, OLE_COLOR itemColor, VARIANT_BOOL* bRet);
STDMETHOD(ClearItems)();
5、属性、方法添加完毕,完成代码。
5.1 图形绘制代码:HRESULT OnDraw(ATL_DRAWINFO& di)
{
if(m_bShow)
{
switch(m_chartType)
{
case ctHistogram:
return DrawHistogram(di);
case ctPieChart:
return DrawPieChart(di);
}
}
return S_FALSE;
}
HRESULT Chart::DrawHistogram(ATL_DRAWINFO &di)
{
RECT& rc = *(RECT*)di.prcBounds;
HPEN hPen = CreatePen(PS_SOLID, 2, RGB(0, 0, 0));
if(NULL == hPen)
return S_FALSE;
SelectObject(di.hdcDraw, hPen);
// draw the coordinate
MoveToEx(di.hdcDraw, rc.left, rc.bottom - 5, NULL);
LineTo(di.hdcDraw, rc.right, rc.bottom - 5);
MoveToEx(di.hdcDraw, rc.left + 5, rc.bottom, NULL);
LineTo(di.hdcDraw, rc.left + 5, rc.top);
DeleteObject(hPen);
hPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
HPEN oldPen = (HPEN)SelectObject(di.hdcDraw, hPen);
// items count
int nNum = m_items.size();
if(0 == nNum)
return S_FALSE;
// origin of the coordinate
POINT ptOrg = { rc.left + 5, rc.bottom - 5 };
// each rectangle's width
DOUBLE dWidth = (DOUBLE)(rc.right - ptOrg.x) / (DOUBLE)(2 * nNum + 1);
// one percent height
DOUBLE dHeight = (DOUBLE)(ptOrg.y - rc.top) / 100.00;
// item's index
int nIdx = 0;
std::list<ItemInfo>::const_iterator iter = m_items.begin();
while(iter != m_items.end())
{
// paint area
RECT rcPaint;
rcPaint.left = ptOrg.x + (2 * nIdx + 1) * dWidth;
rcPaint.right = rcPaint.left + dWidth;
rcPaint.bottom = ptOrg.y;
rcPaint.top = rcPaint.bottom - dHeight * (*iter).percent;
HBRUSH hBrush = CreateSolidBrush((*iter).color);
HBRUSH oldBrush = (HBRUSH)SelectObject(di.hdcDraw, hBrush);
Rectangle(di.hdcDraw, rcPaint.left, rcPaint.top, rcPaint.right, rcPaint.bottom);
SelectObject(di.hdcDraw, oldBrush);
// continue to the next
iter++;
nIdx++;
}
SelectObject(di.hdcDraw, oldPen);
return S_OK;
}
HRESULT Chart::DrawPieChart(ATL_DRAWINFO &di)
{
// make it become a square
RECT& rc = *(RECT*)di.prcBounds;
if((rc.right - rc.left) > (rc.bottom - rc.top))
rc.right = rc.left + rc.bottom - rc.top;
else
rc.bottom = rc.top + rc.right - rc.left;
HPEN hPen = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
if(NULL == hPen)
return S_FALSE;
HPEN oldPen = (HPEN)SelectObject(di.hdcDraw, hPen);
// draw a circle
Ellipse(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom);
const DOUBLE PI = 3.14159265;
const POINT ptCenter = { (rc.left + rc.right) / 2, (rc.top + rc.bottom) / 2 };
const LONG R = ptCenter.x - rc.left;
POINT ptOrg = { rc.right, ptCenter.y };
POINT ptDst = ptOrg;
DOUBLE dAngle = 0.0;
// draw the pie chart one by one
std::list<ItemInfo>::const_iterator iter = m_items.begin();
while(iter != m_items.end())
{
// make the destination of the last time to become the original of this time
ptOrg = ptDst;
// select brush
dAngle += (*iter).percent / 100.0 * 360.0;
HBRUSH hBrush = CreateSolidBrush((*iter).color);
HBRUSH oldBrush = (HBRUSH)SelectObject(di.hdcDraw, hBrush);
// get ptDst
ptDst.x = ptCenter.x + R * cos((360.0 - dAngle) * PI / 180.0);
ptDst.y = ptCenter.y + R * sin((360.0 - dAngle) * PI / 180.0);
// counterclockwise
Pie(di.hdcDraw, rc.left, rc.top, rc.right, rc.bottom, ptOrg.x,
ptOrg.y, ptDst.x, ptDst.y);
SelectObject(di.hdcDraw, oldBrush);
iter++;
}
SelectObject(di.hdcDraw, oldPen);
return S_OK;
}
STDMETHODIMP Chart::Show(VARIANT_BOOL bShow)
{
if(m_bShow != bShow)
{
m_bShow = bShow;
FireViewChange();
}
return S_OK;
}
5.2 添加条目代码:
STDMETHODIMP Chart::AddItem(BSTR itemName, FLOAT itemPersent, OLE_COLOR itemColor, VARIANT_BOOL* bRet)
{
if((itemPersent + GetTotalPercent()) > 100.0)
{
*bRet = S_FALSE;
return S_FALSE;
}
ItemInfo ii = { 0 };
wcscpy_s(ii.name, itemName);
ii.percent = itemPersent;
ii.color = itemColor;
std::list<ItemInfo>::iterator iter = m_items.begin();
while(iter != m_items.end())
{
if(itemName == (*iter).name)
{
*bRet = S_FALSE;
return S_FALSE;
}
iter++;
}
m_items.push_back(ii);
FireViewChange();
return S_OK;
}
STDMETHODIMP Chart::get_chartType(SHORT* pVal)
{
*pVal = (SHORT)m_chartType;
return S_OK;
}
STDMETHODIMP Chart::put_chartType(SHORT newVal)
{
if(newVal == (SHORT)m_chartType)
return S_OK;
switch(newVal)
{
case 0:
m_chartType = ctNull;
break;
case 1:
m_chartType = ctHistogram;
break;
case 2:
m_chartType = ctPieChart;
break;
default:
return S_FALSE;
}
FireViewChange();
return S_OK;
}
二、控件测试程序编写:
控件完成之后,我们需要编写一个测试程序来展现它的样子。
1、创建一个MFC对话框程序。
2、插入我们刚写好的ActiveX控件,完成界面布局。
3、给我们的ActiveX控件关联一个变量。
关联变量完成后,我们可以看到VS自动给我们生成了一个chart.h跟chart.cpp文件。
4、编写代码。
// add item
void CAtlChartTestDlg::OnBnClickedBtnAdd()
{
CString strName, strPercent;
GetDlgItemText( IDC_EDIT_NAME, strName );
GetDlgItemText( IDC_EDIT_PERCENT, strPercent );
if( strName.IsEmpty() || strPercent.IsEmpty() )
{
AfxMessageBox( L"请输入正确的参数!" );
return;
}
DOUBLE dPercent = _ttof( strPercent );
COLORREF colorRef = m_btnColor.GetColor();
m_chart.AddItem( strName, dPercent, colorRef );
}
// clear
void CAtlChartTestDlg::OnBnClickedBtnClear()
{
m_chart.ClearItems();
}
// histogram
void CAtlChartTestDlg::OnBnClickedBtnHistogram()
{
m_chart.put_ChartType( 1 );
}
// pie
void CAtlChartTestDlg::OnBnClickedBtnPie()
{
m_chart.put_ChartType( 2 );
}
三、结果。
下面就是我们控件测试的结果展示:
添加4个条目:{111,12%,绿色},{222,13%,棕色},{333,55%,蓝色},{444,10%,黄色}
好了,完成。写的很简略,因为真的不知道该写什么。代码在这里可以下载:win7下activex控件例子