一直想寫一些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控件例子