1、新建一个类CXPButton,该类继承于CButton
说明:自绘控件说白了就是画图,故而在需要添加的成员变量中可以看到与画图相关的各种数据类型,一般来说成员变量都会在构造函数中初始化,在类的析构函数中销毁。
按钮的实现原理:
(1)在控件初始化的时候为控件添加Owner_Draw的属性。因为在MFC中想要激活控件的自绘功能,要求在该控件的属性中必须包含属性值BS_OWNERDRAW,这一步的具体实现方法可以通过类向导为CXPButton类添加PreSubclassWindow()函数,在该函数中完成属性值的设置。当激活控件的自绘功能之后,每次控件的状态改变之后都会运行函数DrawItem(),这个函数的功能就在于绘制控件在各种状态下的外观。
(2)添加WM_MOUSELEAVE消息函数,当鼠标指针离开按钮时,触发该消息函数,我们在函数中添加代码,通知DrawItem函数鼠标指针已经离开,让按钮重绘。
(3)添加WM_MOUSEHOVER消息函数,当鼠标指针位于按钮之上的时候,触发该消息函数,我们在函数中添加代码,通知DrawItem函数鼠标指针在按钮上,让按钮重绘。
(4)添加DrawItem函数。在该函数中根据按钮的当前状态绘制按钮的外观。可以说绘制按钮的大部分功能都是在这个函数中实现的。在该函数中有一个很关键的指针:LPDRAWITEMSTRUCT
说明:事实上WM_MOUSELEAVE和WM_MOUSEHOVER两个Windows消息是通过WM_MOUSEMOVE消息触发的,而WM_MOUSEMOVE是标准的Windows消息,因此我们可以通过类向导来为CXPButton类添加WM_MOUSEMOVE消息函数。
代码如下:
void CXPButton::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if (!m_bTracking)
{
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.hwndTrack = m_hWnd;
tme.dwFlags = TME_LEAVE | TME_HOVER;
tme.dwHoverTime = 1;
m_bTracking = _TrackMouseEvent(&tme);
}
CButton::OnMouseMove(nFlags, point);
}
这段代码很重要,在其它自绘控件中,如果想触发WM_MOUSELEAVE和WM_MOUSEHOVER消息,也使用类似的方法去实现。
2、接着添加WM_MOUSELEAVE和WM_MOUSEHOVER消息消息函数。在CXPButton类的声明中(即在XPButton.h文件中)找到afx_msg void OnMouseMove(UINT nFlags, CPoint point);的函数声明,紧接其下输入:
afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
afx_msg LRESULT OnMouseHover(WPARAM wParam, LPARAM lParam);
然后在XPButton.cpp文件中找到ON_WM_MOUSEMOVE(),紧接其后输入
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
再就是函数的实现了代码如下:
LRESULT CXPButton::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
m_bOver = FALSE;
m_bTracking = FALSE;
InvalidateRect(NULL, FALSE);
return 0;
}
LRESULT CXPButton::OnMouseHover(WPARAM wParam, LPARAM lParam)
{
m_bOver = TRUE;
InvalidateRect(NULL);
return 0;
}
实际上自绘按钮本身的函数结构都是差不多的,它们显示效果的区别主要取决于代码编写者对GDI作图函数的运用与掌握程度。可以研究一下CXPButton类中DrawItem函数的数据结构,其实只要修改一下其中GDI绘图函数的部分代码,马上又能做出另一个自绘按钮控件了。