首页 > 代码库 > 编写 ATL ActiveX 控件

编写 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控件例子


编写 ATL ActiveX 控件