一直想写一些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; }
5.3 图表样式设置代码:
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控件例子