当前位置:C++技术网 > 资讯 > VC++实现控件自绘的最简源码剖析

VC++实现控件自绘的最简源码剖析

更新时间:2016-04-21 11:22:02浏览次数:1+次

我在之前的三篇文章中,都讲解了控件自绘的过程原理及其源代码。不过,那是别人的,我们写代码需要的是在别人的基础上修改而后变成自己的东西。对于控件的自绘,我们知道需要presubclasswindow函数来传递控件自绘的消息。尽管,之前,我写过一些关于presubclasswindow函数的文章,但是在这里,我还是想再说说这个函数。因为它太重要了!一般情况下,我们可以用precreatewindow函数来实现控件自绘,因为,大多数窗体的创建都是基于CWnd::Create函数的。可是当我们在对话框上创建控件时,就没有这么简单了。很简单,因为,对话框的创建不是基于CWnd::Create函数,而是由对话框资源模板(CreateDlgIndirect)来创建的。对于对话框的创建分析请看《MFC的对话框类CDialog》。在这里,我就不多说了。其实对于这些的分析,我们可以借助VS插件VA来查找函数的源码,从而来分析问题。
对于控件的自绘,首先,就是派生一个基于CButton的类。而后自定义一个Create函数,DrawButton函数。重载presubclasswindow函数与DrawItem函数,最后在派生类中的OnCreate函数里面调用SetWindowRgn函数设定好我们控件的区域。这就是控件自绘时程序执行的几个重要的函数快。

下面,我们先看看实现:

下面先给出源码:

int CMybutton::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CButton::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码
	SetWindowRgn(m_hRgn,true);

	return 0;
}


void CMybutton::PreSubclassWindow()
{
	// TODO: 在此添加专用代码和/或调用基类
	ModifyStyle(0,BS_OWNERDRAW | BS_PUSHBUTTON);
	CButton::PreSubclassWindow();
}


void CMybutton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{

	// TODO:  添加您的代码以绘制指定项

	CDC* pDC = CDC::FromHandle(lpDrawItemStruct -> hDC);
	CRect rect;
	GetClientRect(rect);
	DrawButton(pDC, &rect, lpDrawItemStruct -> itemState);

}


void CMybutton::DrawButton(CDC * pDC, CRect * pRect, UINT state)
{
	// 创建内存设备
	HRGN hBaseRgn = CreateRectRgn(0, 0, 0, 0);
	HBRUSH hBrush;
	COLORREF ltInner;	//左上内存边框的颜色
	ltInner = GetSysColor(COLOR_3DHILIGHT);

	COLORREF rbOuter;	//右下外侧边框的颜色
	rbOuter = GetSysColor(COLOR_3DDKSHADOW);

	HRGN hRgn = CreateRectRgn(0, 0, 0, 0);
	GetWindowRgn(hRgn);

	CString text;
	GetWindowText(text);
	CSize Extent=pDC->GetTextExtent(text);
	CPoint pt=CPoint(m_CenterPoint.x-Extent.cx/2,m_CenterPoint.y-Extent.cy/2); 
	SetTextColor(pDC->GetSafeHdc(),RGB(255,0,0));


	CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
	OffsetRgn(hBaseRgn, 1, 1);
	CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);//得到左上框的部分并上色为红色
	hBrush = CreateSolidBrush(ltInner);
	FillRgn(*pDC, hBaseRgn, hBrush);
	DeleteObject(hBrush);

	CombineRgn(hBaseRgn, hRgn, 0, RGN_COPY);
	OffsetRgn(hBaseRgn, -1, -1);
	CombineRgn(hBaseRgn, hRgn, hBaseRgn, RGN_DIFF);//得到右上框的部分并上色为红色
	hBrush = CreateSolidBrush(rbOuter);
	FillRgn(*pDC, hBaseRgn, hBrush);
	DeleteObject(hBrush);

	if (state & ODS_SELECTED) 
    {
        //向一个点中添加x,y坐标
		pt.Offset(1,1);     
	}   
	TextOut(pDC->GetSafeHdc(), pt.x, pt.y, text, text.GetLength());
}


BOOL CMybutton::Create(LPCTSTR lpszCaption, DWORD dwStyle, const CPoint point, const HRGN hRgn, CWnd* pParentWnd, UINT nID)
{
	// store region in member variable
	DeleteObject(m_hRgn);
	m_hRgn = CreateRectRgn(0, 0, 31, 31);
	CRect box(0, 0, 0, 0);
	if (m_hRgn != 0) 
		CombineRgn(m_hRgn, hRgn, 0, RGN_COPY);

	// make sure that region bounding rect is located in (0, 0)
	GetRgnBox(m_hRgn, &box);

	// update position of region center for caption output
	m_CenterPoint = CPoint(box.Width()/2,box.Height()/2);
	box.OffsetRect(point);

	return CButton::Create(lpszCaption, dwStyle, box, pParentWnd, nID);
}
以上是派生类中cpp文件的代码,其头文件的代码我就不啰嗦了。你自己也应该会!
在Create函数中,主要就是计算出我们自绘的控件的区域的中点,以便,我们在这里输出文字。接下来就是presubclasswindow函数了,在这里,我们设定控件的样式为自绘属性。然后就会调用OnCreate函数块中的SetWindowRgn函数,设定好控件的区域,在此区域中你可以自绘控件。就是调用DrawItem函数了。在DrawItem函数里面,我们调用的是DrawButton函数,这个函数完成控件边框的绘制。其实主要就是
CombineRgn,FillRgn两函数的调用。同时我们利用OffsetRgn函数来实现控件边框的真实化。
好了,我分析的也差不多了。下面就是看你自己了。
这段时间,其实比较迷茫。毕竟大二了,现在学习很蒙。不过还是理清了思路。由于我是学C++的,因此,STL一定要学好,数据结构结合STL把这两块学好了,面试时也就有了底气。其次,我之前比较重视VC,一心搞软件开发。现在也放下来了。开发这一块不急,应该把技术学好,不说精通,也要熟练。因此,在大学面试之前,也就是大三结束时,多线程编程,网络编程以VC++为基础这两块要熟练。希望,我的学习路线对你有帮助。