MFC界面美化之自绘按钮控件的实现,圆角按钮,图片按钮

⌚Time: 2022-09-09 12:06:30

👨‍💻Author: Jack Ge

对于窗体自绘,在它的onpaint函数中进行实现,对于控件的自绘,会有一个DrawItem消息,它会调用控件的DrawItem函数进行绘制。

所有需要自绘的控件,首先设置它的属性,Owner Draw为true

用按钮控件作为例子,有两种方法实现自绘,第一种是在窗体的OnDrawItem函数中实现

首先对于窗体资源,属性中,找到它的消息列表,找到WM_DRAWITEM,添加它的OnDrawItem响应函数




void CTestOwnDrawDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)

{

    // TODO: 在此添加消息处理程序代码和/或调用默认值

    CDC ButtonDC;

    CBitmap bitmapTrans;

    BITMAP bmp;

    CDC mem;

    CRect rc;

    //得到用于绘制按钮的DC

    ButtonDC.Attach(lpDrawItemStruct->hDC);

    //准备用于向按钮区域传输位图

    mem.CreateCompatibleDC(&ButtonDC);

    //获取按钮所占的矩形大小

    rc=lpDrawItemStruct->rcItem;



    //判断控件ID是否是所需要绘制的控件

    if(nIDCtl == IDOK || nIDCtl == IDCANCEL){

        //判断按钮状态 ODS_SELECTED是选中状态标志,判断此标志位为真则,绘制选中状态图像

        if (lpDrawItemStruct->itemState & ODS_SELECTED)

        {



            bitmapTrans.LoadBitmap(IDB_BITMAP1);

            bitmapTrans.GetBitmap(&bmp);

            CBitmap *old=mem.SelectObject(&bitmapTrans);

            ButtonDC.SetStretchBltMode(COLORONCOLOR);

            ButtonDC.StretchBlt(rc.left,rc.top,rc.Width(),rc.Height(),&mem,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);

            mem.SelectObject(old);

            bitmapTrans.DeleteObject();

            //设置透明背景

            ButtonDC.SetBkMode(TRANSPARENT);

            //设置文字颜色

            ButtonDC.SetTextColor(RGB(0,0,0));

            CString strTemp;

            GetDlgItem(nIDCtl)->GetWindowText(strTemp);

            //绘制按钮文字

            ButtonDC.DrawText(strTemp,&rc,DT_CENTER|DT_VCENTER|DT_SINGLELINE);



            mem.DeleteDC();

            ButtonDC.DeleteDC();

        }else{

            //非选中状态,绘制另一种图像

            bitmapTrans.LoadBitmap(IDB_BITMAP2);

            bitmapTrans.GetBitmap(&bmp);

            CBitmap *old=mem.SelectObject(&bitmapTrans);

            ButtonDC.SetStretchBltMode(COLORONCOLOR);

            ButtonDC.StretchBlt(rc.left,rc.top,rc.Width(),rc.Height(),&mem,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);

            mem.SelectObject(old);

            bitmapTrans.DeleteObject();



            //设置透明背景

            ButtonDC.SetBkMode(TRANSPARENT);

            //设置文字颜色

            ButtonDC.SetTextColor(RGB(0,0,0));

            CString strTemp;

            GetDlgItem(nIDCtl)->GetWindowText(strTemp);

            //绘制按钮文字

            ButtonDC.DrawText(strTemp,&rc,DT_CENTER|DT_VCENTER|DT_SINGLELINE);



            mem.DeleteDC();

            ButtonDC.DeleteDC();

        }

        

    //绘制另外两个按钮的样式

    }else if(nIDCtl == IDC_BUTTON1 || nIDCtl == IDC_BUTTON2){

        //判断按钮状态是否点击

        if (lpDrawItemStruct->itemState & ODS_SELECTED)

        {



            //设置画笔 实线,宽度2,白色

            CPen pen(PS_SOLID,2,RGB(255,255,255));

            //设置画刷

            COLORREF color = RGB(176,89,35);

            CBrush brush(color);

            //选择画笔(画轮廓)

            CPen *pOldPen = ButtonDC.SelectObject(&pen);

            //选择画刷(填充色)

            CBrush *pOldBrush = ButtonDC.SelectObject(&brush);

                

            //ButtonDC.SelectClipRgn(&h_rgn);

            //画出圆角矩形按钮形状

            ButtonDC.RoundRect(0,0,rc.Width(),rc.Height(),rc.Width()/2,rc.Height());

            //设置透明背景

            ButtonDC.SetBkMode(TRANSPARENT);

            //设置文字颜色

            ButtonDC.SetTextColor(RGB(255,255,255));

            CString strTemp;

            GetDlgItem(nIDCtl)->GetWindowText(strTemp);

            //绘制按钮文字

            ButtonDC.DrawText(strTemp,&rc,DT_CENTER|DT_VCENTER|DT_SINGLELINE);



            //创建按钮同样的圆角矩型区域

            CRgn h_rgn;

            h_rgn.CreateRoundRectRgn(0,0,rc.Width(),rc.Height(),rc.Width()/2,rc.Height());

            //设置窗口区域(区域外点击按钮无效)

            GetDlgItem(nIDCtl)->SetWindowRgn(h_rgn,TRUE);

            //释放资源

            ButtonDC.SelectObject(pOldPen);

            ButtonDC.SelectObject(pOldBrush);

            pen.DeleteObject();

            brush.DeleteObject();

            ButtonDC.DeleteDC();

        }else{

            //设置画笔

            CPen pen(PS_SOLID,2,RGB(0,255,255));

            //设置画刷

            COLORREF color = RGB(0,149,182);

            CBrush brush(color);

            //选择画笔(画轮廓)

            CPen *pOldPen = ButtonDC.SelectObject(&pen);

            //选择画刷(填充色)

            CBrush *pOldBrush = ButtonDC.SelectObject(&brush);



            //ButtonDC.SelectClipRgn(&h_rgn);

            //画出圆角矩形按钮形状

            ButtonDC.RoundRect(0,0,rc.Width(),rc.Height(),rc.Width()/2,rc.Height());

            //设置透明背景

            ButtonDC.SetBkMode(TRANSPARENT);

            //设置文字颜色

            ButtonDC.SetTextColor(RGB(255,255,255));

            CString strTemp;

            GetDlgItem(nIDCtl)->GetWindowText(strTemp);

            //绘制按钮文字

            ButtonDC.DrawText(strTemp,&rc,DT_CENTER|DT_VCENTER|DT_SINGLELINE);



            //创建按钮同样的圆角矩型区域

            CRgn h_rgn;

            h_rgn.CreateRoundRectRgn(0,0,rc.Width(),rc.Height(),rc.Width()/2,rc.Height());

            //设置窗口区域(区域外点击按钮无效)

            GetDlgItem(nIDCtl)->SetWindowRgn(h_rgn,TRUE);

            

            //释放资源

            ButtonDC.SelectObject(pOldPen);

            ButtonDC.SelectObject(pOldBrush);

            pen.DeleteObject();

            brush.DeleteObject();

            ButtonDC.DeleteDC();

        }



    }

    CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);

}


第二种方法是自定义类,覆写DrawItem函数

右键解决方案,添加,类。添加一个继承于CButton的子类

为了引用图片资源文件,包含头文件


#include "resource.h"

在类视图中,右键该类,属性,在属性中找到重写,DrawItem




void CustomButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)

{

    CDC ButtonDC;

    CBitmap bitmapTrans;

    BITMAP bmp;

    CDC mem;

    CRect rc;

    //得到用于绘制按钮的DC

    ButtonDC.Attach(lpDrawItemStruct->hDC);

    //准备用于向按钮区域传输位图

    mem.CreateCompatibleDC(&ButtonDC);

    //获取按钮所占的矩形大小

    rc=lpDrawItemStruct->rcItem;

    //判断按钮状态显示不同的图像 ODS_FOCUS是获取到焦点的状态

    if(lpDrawItemStruct->itemState & ODS_FOCUS){



        bitmapTrans.LoadBitmap(IDB_BITMAP4);

        bitmapTrans.GetBitmap(&bmp);

        CBitmap *old=mem.SelectObject(&bitmapTrans);

        ButtonDC.SetStretchBltMode(COLORONCOLOR);

        ButtonDC.StretchBlt(rc.left,rc.top,rc.Width(),rc.Height(),&mem,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);

        mem.SelectObject(old);

        bitmapTrans.DeleteObject();

    }else{

        bitmapTrans.LoadBitmap(IDB_BITMAP3);

        bitmapTrans.GetBitmap(&bmp);

        CBitmap *old=mem.SelectObject(&bitmapTrans);

        ButtonDC.SetStretchBltMode(COLORONCOLOR);

        ButtonDC.StretchBlt(rc.left,rc.top,rc.Width(),rc.Height(),&mem,0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);

        mem.SelectObject(old);

        bitmapTrans.DeleteObject();

    }

    

    mem.DeleteDC();

    ButtonDC.DeleteDC();

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

}


在主窗口类中,包含自定义类的头文件


#include "CustomButton.h"

定义一个按钮变量


private:

    CustomButton m_customButton;


在DoDataExchange函数中建立此变量与窗体的按钮控件的关联


void CTestOwnDrawDlg::DoDataExchange(CDataExchange* pDX)

{

    CDialog::DoDataExchange(pDX);

    //IDC_BUTTON3是窗体控件的资源ID,m_customButton是按钮变量

    DDX_Control(pDX, IDC_BUTTON3, m_customButton);

}

加背景后图片自绘控件被覆盖的问题

在窗口的OnPaint函数中添加背景图片,之后运行程序,你会发现只有一张背景图片,而按钮被覆盖

解决办法是窗口属性的Clip Child设置为true,代表父窗口不绘制子窗口区域,这样父窗口图片不会遮挡住按钮控件了