MFC自定义按钮的实现

    学习MFC的都知道,我们要想改变对话框和控件的背景以及文本颜色,可以响应OnCtlColor消息,在这个函数里面进行相应的设置,如下所示:

HBRUSH StatusBar::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
 HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

 // TODO:  Change any attributes of the DC here 

 // TODO:  Return a different brush if the default is not desired
 switch (nCtlColor)
 {
 case CTLCOLOR_BTN:
 case CTLCOLOR_STATIC:
  pDC->SetBkMode(TRANSPARENT);
  pDC->SetTextColor(RGB(200, 255, 255));
 case CTLCOLOR_DLG:
  return (HBRUSH)m_brBack.GetSafeHandle();
 }
 return hbr;
}

    但是对于按钮来说上述方法是不起作用的,只能寻找其他解决办法。实际上,要想改变按钮的背景色和文本颜色,需要使用CButton类的一个成员函数DrawItem,该函数的声明如下:// Overridables (for owner draw only)

 virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);

    这个函数是一个虚函数,当一个自绘制按钮在绘制时候,框架将会调用这个虚函数。因此,如果想要实现一个自绘按钮,就应该重载这个虚函数。该函数的参数是DRAWITEMSTRUCT结构类型,定义如下:

typedef struct tagDRAWITEMSTRUCT {
    UINT        CtlType;
    UINT        CtlID;
    UINT        itemID;
    UINT        itemAction;
    UINT        itemState;
    HWND        hwndItem;
    HDC         hDC;
    RECT        rcItem;
    ULONG_PTR   itemData;
} DRAWITEMSTRUCT;

    该结构体有一个hDC成员,指向将要绘制的按钮的DC。为了绘制这个按钮,可以向该DC中选入自定义的颜色、画刷等对象。了解了这些知识,下面开始真正的自绘按钮,首先看一下效果,效果如下:

    

    这里列出了按钮的几种状态,第一种是正常状态,第二种是按下之后的状态,第三中是不同的背景颜色,这里设置了两种不同的背景颜色,实际上这里的背景是图片,所以看起来会有立体的感觉!

    

以下是.h文件源代码,看起来应该不难^_^:

class CPicButton : public CButton
{
public:
 CPicButton(void);
 ~CPicButton(void);

public:
 enum { MAX_BTN_COLOR = 3 };
 enum { COLOR_NORMAL = 0, COLOR_FOCUSED = 1, COLOR_PRESSED = 2 }; //按钮的三种状态

 COLORREF m_crForeColor[MAX_BTN_COLOR];

 UINT  m_nTypeStyle;
 int   m_nColorType;

 BOOL  m_bMouseOnButton; // Is mouse over the button?
 BOOL  m_bIsPressed;   // Is button pressed?
 BOOL  m_bIsFocused;  // Is button focused?
 BOOL  m_bIsDisabled;  // Is button disabled?

 CString  m_sFontName; //字体名字
 int   m_nFontWidth, m_nFontHeight; //字体大小
 BOOL  m_bPointFont;

public:
 virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); //这里是重点,通过调用Invalidate()可以马上重绘按钮,从而调用这个函数
 virtual BOOL PreTranslateMessage(MSG *pMsg);

 void SetForeColor(int nIndex, COLORREF color, BOOL bRepaint = TRUE);//设置前景色
 void SetColorType(int nColorIdx);                           //设置背景颜色的类别,在这里代表两种背景图片
 void SetCaptionFont(CString sFontName, int nWidth, int nHeight, BOOL bPointFont=FALSE);

protected:
 virtual void PreSubclassWindow();//在这里面修改按钮的样式
 virtual void DrawBackground(CDC *dc, int nWidth, int nHeight);//绘制背景
 virtual void DrawBtnCaption(CDC *dc, int nWidth, int nHeight);//绘制字体

private:
 void FreeResources();
 void CancelHover();

public:
 afx_msg BOOL OnClicked();//点击事件
 afx_msg void OnKillFocus(CWnd* pNewWnd);
 afx_msg void OnMouseMove(UINT nFlags, CPoint point);//鼠标移动事件
 afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
 afx_msg LRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);

DECLARE_MESSAGE_MAP()
};

下面是.cpp文件源码:
#include "stdafx.h"
#include "PicBtn.h"
#include "DrawingTools.h"
#include "Resource.h"
CPicButton::CPicButton(void)
{
 m_crForeColor[COLOR_NORMAL]  = RGB(0, 0, 0);
 m_crForeColor[COLOR_FOCUSED] = RGB(240, 240, 240);
 m_crForeColor[COLOR_PRESSED] = RGB(255, 255, 255);
 m_nColorType  = 0;  // Brown or Red
 m_bMouseOnButton = FALSE;
 m_bIsPressed  = FALSE;
 m_bIsFocused  = FALSE;
 m_bIsDisabled  = FALSE;
 m_nFontHeight  = 17;
 m_nFontWidth  = 6;
 m_bPointFont  = FALSE;
 m_sFontName   = _T("Arial");
}
CPicButton::~CPicButton(void)
{
 FreeResources();
}
void CPicButton::FreeResources()
{
}
void CPicButton::CancelHover()
{
 if (m_bMouseOnButton)
 {
  m_bMouseOnButton = FALSE;
  Invalidate();
 }
}
            
void CPicButton::PreSubclassWindow()
{
#if defined(_CE_VERSION)
 ModifyStyle(0, BS_OWNERDRAW, SWP_FRAMECHANGED);
#else
 UINT nBS = GetButtonStyle();
                     
 m_nTypeStyle = nBS & BS_TYPEMASK;
 if (m_nTypeStyle == BS_DEFPUSHBUTTON)
 {
  m_nTypeStyle = BS_PUSHBUTTON;
 }
// You should not set the Owner Draw before this call 
 // (don't use the resource editor "Owner Draw" or 
 // ModifyStyle(0, BS_OWNERDRAW) before calling PreSubclassWindow())
 ASSERT(m_nTypeStyle != BS_OWNERDRAW);
//Switch to owner-draw
 ModifyStyle(BS_TYPEMASK, BS_OWNERDRAW, SWP_FRAMECHANGED);
#endif
 CButton::PreSubclassWindow();
}
BOOL CPicButton::PreTranslateMessage(MSG* pMsg)
{
 if (pMsg->message == WM_LBUTTONDBLCLK)
  pMsg->message = WM_LBUTTONDOWN;
 return CButton::PreTranslateMessage(pMsg);
}
void CPicButton::SetColorType(int nColorIdx)
{
 m_nColorType = nColorIdx;
}
void CPicButton::SetCaptionFont(CString sFontName, int nWidth, int nHeight, BOOL bPointFont)
{
 m_sFontName  = sFontName;
 m_nFontWidth = nWidth;
 m_nFontHeight = nHeight;
 m_bPointFont = bPointFont;
 Invalidate();
}
BEGIN_MESSAGE_MAP(CPicButton, CButton)
 ON_WM_KILLFOCUS()
 ON_CONTROL_REFLECT_EX(BN_CLICKED, OnClicked)  //消息反射
 ON_WM_ACTIVATE()
#ifndef _CE_VERSION
 ON_WM_MOUSEMOVE()
 ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
#endif
END_MESSAGE_MAP()
#ifndef _CE_VERSION
void CPicButton::OnMouseMove(UINT nFlags, CPoint point)
{
 CWnd   *wndUnderMouse = NULL;
 CWnd   *wndActive = this;
 TRACKMOUSEEVENT csTME;
 CButton::OnMouseMove(nFlags, point);
 ClientToScreen(&point);
 wndUnderMouse = WindowFromPoint(point);//获得点下面的组件
 // If the mouse enter the button with the left button pressed then do nothing
 if (nFlags & MK_LBUTTON && m_bMouseOnButton == FALSE)
  return;
 if (wndUnderMouse && wndUnderMouse->m_hWnd == m_hWnd && wndActive)
 {
  if (!m_bMouseOnButton)
  {
   m_bMouseOnButton = TRUE;
   Invalidate();
   csTME.cbSize = sizeof(csTME);
   csTME.dwFlags = TME_LEAVE;
   csTME.hwndTrack = m_hWnd;
   ::_TrackMouseEvent(&csTME);//发送鼠标离开的消息,见我之前的博客
  }
 }
 else
  CancelHover();
}
LRESULT CPicButton::OnMouseLeave(WPARAM wParam, LPARAM lParam)
{
 CancelHover();
 return 0;
}
#endif
BOOL CPicButton::OnClicked()
{ 
 SetFocus();//设置焦点
 Invalidate();
 return FALSE;
}
void CPicButton::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
 CDC   *pDC = CDC::FromHandle(lpDIS->hDC); //获取CDC
 int   nWidth, nHeight;
 CRect  itemRect = lpDIS->rcItem;
  nWidth = itemRect.Width();
 nHeight = itemRect.Height();
 m_bIsPressed = (lpDIS->itemState & ODS_SELECTED);//是不是已经按下
 m_bIsFocused = (lpDIS->itemState & ODS_FOCUS);   //是不是获得了焦点
 m_bIsDisabled = (lpDIS->itemState & ODS_DISABLED); //是不是没使能
 pDC->SetBkMode(TRANSPARENT);//设置背景透明
 DrawBackground(pDC, nWidth, nHeight);//画背景
 DrawBtnCaption(pDC, nWidth, nHeight); //画字体
}
void CPicButton::DrawBackground(CDC *dc, int nWidth, int nHeight)
{
 if (m_bIsPressed)
 {
  if (m_nColorType == 0)
   ::DrawBMP(dc,IDB_BTN_YP, 0, 0); //
  else
   ::DrawBMP(dc,IDB_BTN_RP, 0, 0);//画红色按下背景
 }
 else
 {
  if (m_nColorType == 0)
   ::DrawBMP(dc,IDB_BTN_Y, 0, 0);
  else
   ::DrawBMP(dc,IDB_BTN_R, 0, 0);//画红色背景
 }
}
void CPicButton::DrawBtnCaption(CDC *dc, int nWidth, int nHeight)
{
 CString   str;
 CRect   rect;
 int    nColorIndex;
 CFont   font, *old_font;
 if (m_bIsPressed)
  nColorIndex = COLOR_PRESSED;
 else if (m_bMouseOnButton)
  nColorIndex = COLOR_FOCUSED;
 else
  nColorIndex = COLOR_NORMAL;
 if (m_bPointFont)
  font.CreatePointFont(m_nFontHeight * 10, m_sFontName);
 else
 {
  font.CreateFont(m_nFontHeight, m_nFontWidth, 0, 0, FW_BOLD, 0, 0, 0,
   DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS,
   DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, m_sFontName);
 }
 GetWindowText(str);
 rect = CRect(2, 2, nWidth - 4, nHeight - 4);
 old_font = dc->SelectObject(&font);
 dc->SetTextColor(m_crForeColor[nColorIndex]);
 //显示字体
 dc->DrawText(str, str.GetLength(), &rect, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
 dc->SelectObject(old_font);
 font.DeleteObject();
}
void CPicButton::SetForeColor(int nIndex, COLORREF crColor, BOOL bRepaint)
{
 m_crForeColor[nIndex] = crColor;
 if (bRepaint) 
  Invalidate();
}
void CPicButton::OnKillFocus(CWnd * pNewWnd)
{
 CButton::OnKillFocus(pNewWnd);
 CancelHover();//当失去焦点的时候,需要改变字体颜色,需要重绘,调用Invalidate();
}
void CPicButton::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
{
 CButton::OnActivate(nState, pWndOther, bMinimized);
 if (nState == WA_INACTIVE)  //当失去焦点的时候,需要改变字体颜色,需要重绘,调用Invalidate();
  CancelHover();
}

 自定义按钮类已经写好了,那么怎么和对话框中的按钮关联起来呢?方法如下:
在.h中:

#include "PicBtn.h"

CPicButton m_btnExit;

在.cpp中:

void CFiveChessDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialogEx::DoDataExchange(pDX);
 //这里就把我们对话框中的按钮和我们自定义的按钮类关联起来了
 DDX_Control(pDX, IDC_BUTTON_Quit, m_btnExit);
}

//设置按钮的显示字体,背景颜色等等,这个函数可以放在OnInitDialog函数里面调用

void CFiveChessDlg::InitButton(){

 m_btnExit.SetCaptionFont(_T("SimHei"), 0, 12, TRUE);
 m_btnExit.SetWindowText(_T("退出"));
 m_btnExit.SetColorType(1);

}

       然后就和正常的按钮一样响应点击事件就可以了!

    通过以上自定义按钮的方法进行扩展,可以做出我们需要的效果,比如在按钮上显示图片和文字或者就纯粹的图片按钮等等,QQ界面就用了很多的图片按钮!

    下面是我自定义的另外一种按钮:

    

      这个就比较简单了,代码和之前的那个图片背景的差不多,只是它这个背景有三种颜色,而之前的是字体有三种颜色而已!

    顺便讲一下上面这个对话框顶部尖尖的地方以及按钮等间隔和等大小是怎么实现的,代码如下:

//按钮等间隔和等大小的实现

void CSetting_Menu::UpdateLayout()
{
 CRect rect1,rect2;
 rect1.top = 0;
 rect1.left = 0;
 rect1.right = rect1.left + 250;
 rect1.bottom = rect1.top + 315;
 MoveWindow(rect1);//设置对话框的大小,固定的
 if (GetDlgItem(IDC_BUTTON_General_Setting))
 {
  rect1.left = 5;
  rect1.right = rect1.left+240;
  rect1.top = 15;
  rect1.bottom = rect1.top + 45;
  GetDlgItem(IDC_BUTTON_General_Setting)->MoveWindow(rect1); //移动按钮到这个位置
 }
 if (GetDlgItem(IDC_BUTTON_COM_Port_Setting))
 {
  rect1.left = 5;
  rect1.right = rect1.left + 240;
  rect1.top = 5*4+45;
  rect1.bottom = rect1.top + 45;
  GetDlgItem(IDC_BUTTON_COM_Port_Setting)->MoveWindow(rect1);
 }
 if (GetDlgItem(IDC_BUTTON_DataFile_Setting))
 {
  rect1.left = 5;
  rect1.right = rect1.left + 240;
  rect1.top = 5*5 + 45*2;
  rect1.bottom = rect1.top + 45;
  GetDlgItem(IDC_BUTTON_DataFile_Setting)->MoveWindow(rect1);
 }
 if (GetDlgItem(IDC_BUTTON_General_Command))
 {
  rect1.left = 5;
  rect1.right = rect1.left + 240;
  rect1.top = 5*6 + 45*3;
  rect1.bottom = rect1.top + 45;
  GetDlgItem(IDC_BUTTON_General_Command)->MoveWindow(rect1);
 }
 if (GetDlgItem(IDC_BUTTON_AutoClib_Setting))
 {
  rect1.left = 5;
  rect1.right = rect1.left + 240;
  rect1.top = 5*7 + 45*4;
  rect1.bottom = rect1.top + 45;
  GetDlgItem(IDC_BUTTON_AutoClib_Setting)->MoveWindow(rect1);
 }
 if (GetDlgItem(IDC_BUTTON_ACC_GYRO_Scale))
 {
  rect1.left = 5;
  rect1.right = rect1.left + 240;
  rect1.top = 5*8 + 45*5;
  rect1.bottom = rect1.top + 45;
  GetDlgItem(IDC_BUTTON_ACC_GYRO_Scale)->MoveWindow(rect1);
 }
}
以上代码就是对对话框的进行了一个布局,这样就看起来比较规整,按钮的大小和间隔都是一样的!

//对话框顶部凸起部分的实现

CPoint pts[7] = { CPoint(0, 10), CPoint(40, 10), CPoint(50, 0), CPoint(60, 10), CPoint(250, 10), CPoint(250, 315), CPoint(0, 315)};

//创建一个多边形区域,以上面定义的七个顶点为边界
 HRGN poloy = CreatePolygonRgn(pts, 7, ALTERNATE);
 CRgn rgn;
 rgn.Attach(poloy);
 SetWindowRgn(rgn,TRUE); //设置窗口的多边形形状
 rgn.Detach();

以上代码就可以把对话框设置成各种形状了,是不是很Cool ^_^。













  • 8
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值