編寫 ATL ActiveX 控件

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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章