对于窗体自绘,在它的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的子类

为了引用图片资源文件,包含头文件
在类视图中,右键该类,属性,在属性中找到重写,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: 添加您的代码以绘制指定项
}
在主窗口类中,包含自定义类的头文件
定义一个按钮变量
在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,代表父窗口不绘制子窗口区域,这样父窗口图片不会遮挡住按钮控件了
