要实现定制按钮的功能,可以从MFC的CButton派生出你自己的一个子类,比如CIndicatorButton,其定义如下:
class CIndicatorButton : public CButton
{
// Construction
public:
CIndicatorButton();
// Attributes
public:
// Operations
public:
void SetBkColor(COLORREF crBk); // 设置背景颜色
COLORREF GetBkColor() { return m_crBk; } // 得到背景色(用CBrush画时的颜色)
void SetForeColor(COLORREF crFore); // 设置前景颜色(用CPen画时的颜色)
COLORREF GetForeColor() { return m_crFore; } // 得到前景色
// 设置超级连接标志
void SetHypertextFlag(BOOL bIsHypertext = TRUE) { m_bIsHypertext = bIsHypertext; }
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CIndicatorButton)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); // 在这个函数中按你的意愿去画按钮
protected:
virtual void PreSubclassWindow(); // 在这个函数中给按钮加入自画风格
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CIndicatorButton();
// Generated message map functions
protected:
//{{AFX_MSG(CIndicatorButton)
// NOTE - the ClassWizard will add and remove member functions here.
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
COLORREF m_crBk;
COLORREF m_crFore;
HCURSOR m_hCursor; // 超级连接时的光标
HCURSOR m_hOldCursor;// 正常时的光标
BOOL m_bIsHypertext; // 如果是超级连接文本
};
代码如下:
// 构造函数,用来初始化变量
CIndicatorButton::CIndicatorButton()
{
m_crBk = RGB(0, 0, 255); // 初始化背景色为蓝色
m_crFore = RGB(255, 255, 0);// 初始化前景色为黄色
}
// 按钮子类化之前调用的虚拟函数,在这个函数中修改按钮风格
void CIndicatorButton::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
UINT uStyle = GetButtonStyle(); // 得到按钮的愿风格
SetButtonStyle(uStyle | BS_OWNERDRAW);// 加入自画风格
CButton::PreSubclassWindow();
}
// 设置背景色
void CIndicatorButton::SetBkColor(COLORREF crBk)
{
m_crBk = crBk;
Invalidate(); // 通知按钮重绘自己
}
// 设置前景色
void CIndicatorButton::SetForeColor(COLORREF crFore)
{
m_crFore = crFore;
Invalidate();
}
// 根据鼠标的移动来改变按钮的外观
void CIndicatorButton::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CRect rcItem;
GetClientRect(rcItem);
if (rcItem.PtInRect(point))
{
// 只在第一次重绘,否则会闪烁
if (this != GetCapture())
{
SetCapture();
m_crBk = RGB(0, 0, 0);
Invalidate();
}
if (m_bIsHypertext)
{
if (NULL == m_hCursor)
m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR);
m_hOldCursor = ::SetCursor(m_hCursor);
}
}
else
{
ReleaseCapture();
m_crBk = RGB(0, 0, 255);
if (m_bIsHypertext)
{
if (NULL != m_hOldCursor)
::SetCursor(m_hOldCursor);
}
Invalidate();
}
CButton::OnMouseMove(nFlags, point);
}
// 这个函数是关键函数,在这里你几乎可以实现你想要的任何按钮
// DRAWITEMSTRUCT 结构请参看MSDN文档
void CIndicatorButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: Add your code to draw the specified item
// 如果不是按钮不则不进行任何操作
if (lpDrawItemStruct->CtlType != ODT_BUTTON)
return ;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC); // 将句柄的DC转化为MFC类,以方便后续操作
CString strCaption; // 按钮标题
GetWindowText(strCaption);// 得到按钮标题,以备后续操作可以在DC上重画按钮标题
// 设置背景模式为透明,否则在画按钮标题时,标题的背景会部分地覆盖掉按钮的背景
pDC->SetBkMode(TRANSPARENT);
// 在这里更改按钮的背景色,当然还有其它的办法,反正只要在这个DC上涂上你所要的背景色即可
pDC->FillRect(&lpDrawItemStruct->rcItem, &CBrush(RGB(0, 0, 255)));
pDC->SetTextColor(m_crFore); // 设置前景色
// 画按钮标题,水平和垂直都居中。当然你也可以用 TextOut等函数来实现,反正你把DC当作一个画布就是了,你想怎么画就怎么画
// 如果要使DT_VCENTER(垂直居中)有效,必须同时设置DT_SINGLELINE(单行)风格
pDC->DrawText(strCaption, lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
// 按钮具有焦点时,画聚焦矩形
if (ODS_FOCUS == itemState || ODA_FOCUS == itemAction)
{
CRect rcFocus(rcItem);// 聚焦矩形
rcFocus.DeflateRect(2, 2); // 聚焦矩形要小些
pDC->DrawFocusRect(&rcFocus);
}
// 余下的自己去完成吧,比如你可以根据鼠标的移动来改变颜色字体等,但要注意不要随意地重绘,
// 因为如果随意地重绘会产生严重的闪烁,所以只有在必要的时候进行重绘,比如在鼠标在进入时重绘次,在没有
// 出来重入之前就不重绘等等
}
class CIndicatorButton : public CButton
{
// Construction
public:
CIndicatorButton();
// Attributes
public:
// Operations
public:
void SetBkColor(COLORREF crBk); // 设置背景颜色
COLORREF GetBkColor() { return m_crBk; } // 得到背景色(用CBrush画时的颜色)
void SetForeColor(COLORREF crFore); // 设置前景颜色(用CPen画时的颜色)
COLORREF GetForeColor() { return m_crFore; } // 得到前景色
// 设置超级连接标志
void SetHypertextFlag(BOOL bIsHypertext = TRUE) { m_bIsHypertext = bIsHypertext; }
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CIndicatorButton)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); // 在这个函数中按你的意愿去画按钮
protected:
virtual void PreSubclassWindow(); // 在这个函数中给按钮加入自画风格
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CIndicatorButton();
// Generated message map functions
protected:
//{{AFX_MSG(CIndicatorButton)
// NOTE - the ClassWizard will add and remove member functions here.
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
private:
COLORREF m_crBk;
COLORREF m_crFore;
HCURSOR m_hCursor; // 超级连接时的光标
HCURSOR m_hOldCursor;// 正常时的光标
BOOL m_bIsHypertext; // 如果是超级连接文本
};
代码如下:
// 构造函数,用来初始化变量
CIndicatorButton::CIndicatorButton()
{
m_crBk = RGB(0, 0, 255); // 初始化背景色为蓝色
m_crFore = RGB(255, 255, 0);// 初始化前景色为黄色
}
// 按钮子类化之前调用的虚拟函数,在这个函数中修改按钮风格
void CIndicatorButton::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
UINT uStyle = GetButtonStyle(); // 得到按钮的愿风格
SetButtonStyle(uStyle | BS_OWNERDRAW);// 加入自画风格
CButton::PreSubclassWindow();
}
// 设置背景色
void CIndicatorButton::SetBkColor(COLORREF crBk)
{
m_crBk = crBk;
Invalidate(); // 通知按钮重绘自己
}
// 设置前景色
void CIndicatorButton::SetForeColor(COLORREF crFore)
{
m_crFore = crFore;
Invalidate();
}
// 根据鼠标的移动来改变按钮的外观
void CIndicatorButton::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CRect rcItem;
GetClientRect(rcItem);
if (rcItem.PtInRect(point))
{
// 只在第一次重绘,否则会闪烁
if (this != GetCapture())
{
SetCapture();
m_crBk = RGB(0, 0, 0);
Invalidate();
}
if (m_bIsHypertext)
{
if (NULL == m_hCursor)
m_hCursor = AfxGetApp()->LoadCursor(IDC_CURSOR);
m_hOldCursor = ::SetCursor(m_hCursor);
}
}
else
{
ReleaseCapture();
m_crBk = RGB(0, 0, 255);
if (m_bIsHypertext)
{
if (NULL != m_hOldCursor)
::SetCursor(m_hOldCursor);
}
Invalidate();
}
CButton::OnMouseMove(nFlags, point);
}
// 这个函数是关键函数,在这里你几乎可以实现你想要的任何按钮
// DRAWITEMSTRUCT 结构请参看MSDN文档
void CIndicatorButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: Add your code to draw the specified item
// 如果不是按钮不则不进行任何操作
if (lpDrawItemStruct->CtlType != ODT_BUTTON)
return ;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC); // 将句柄的DC转化为MFC类,以方便后续操作
CString strCaption; // 按钮标题
GetWindowText(strCaption);// 得到按钮标题,以备后续操作可以在DC上重画按钮标题
// 设置背景模式为透明,否则在画按钮标题时,标题的背景会部分地覆盖掉按钮的背景
pDC->SetBkMode(TRANSPARENT);
// 在这里更改按钮的背景色,当然还有其它的办法,反正只要在这个DC上涂上你所要的背景色即可
pDC->FillRect(&lpDrawItemStruct->rcItem, &CBrush(RGB(0, 0, 255)));
pDC->SetTextColor(m_crFore); // 设置前景色
// 画按钮标题,水平和垂直都居中。当然你也可以用 TextOut等函数来实现,反正你把DC当作一个画布就是了,你想怎么画就怎么画
// 如果要使DT_VCENTER(垂直居中)有效,必须同时设置DT_SINGLELINE(单行)风格
pDC->DrawText(strCaption, lpDrawItemStruct->rcItem, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
// 按钮具有焦点时,画聚焦矩形
if (ODS_FOCUS == itemState || ODA_FOCUS == itemAction)
{
CRect rcFocus(rcItem);// 聚焦矩形
rcFocus.DeflateRect(2, 2); // 聚焦矩形要小些
pDC->DrawFocusRect(&rcFocus);
}
// 余下的自己去完成吧,比如你可以根据鼠标的移动来改变颜色字体等,但要注意不要随意地重绘,
// 因为如果随意地重绘会产生严重的闪烁,所以只有在必要的时候进行重绘,比如在鼠标在进入时重绘次,在没有
// 出来重入之前就不重绘等等
}