在MFC中Button控件不能通过OnCtlColor()函数对外观做太多的改变。
HBRUSH C按钮控件自绘Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
switch (pWnd->GetDlgCtrlID())
{
case IDC_BUTTON1:
{
pDC->SetBkMode(TRANSPARENT);
pDC->SetBkColor(RGB(255, 0, 255));
pDC->SetTextColor(RGB(255, 0, 0));
static CBrush brush(RGB(255, 0, 255));
return brush;
}
break;
return hbr;
}
}
欲设置按钮的背景为紫色,文字为红色,但是通过OnCtlColor()函数并不能实现预期的结果
为了能够修改Button控件字体、字体大小、背景色、背景图片,需要对Button控件进行自绘。
设置Button控件的自绘属性
方法一:在资源视图中将所有者描述设置为true
方法二:在OnInitDialog函数里给按钮添加BS_OWNERDRAW风格
GetDlgItem(IDC_BUTTON1)->ModifyStyle(0, BS_OWNERDRAW);
建立一个以CButton为基类的派生类并重载DrawItem()函数
通过类向导添加DrawItem()函数
为按钮添加关联变量
变量类型将CButton改成MyButton
MyButton类的设计
#pragma once
#include <afxwin.h>
class MyButton :
public CButton
{
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
public:
//三种颜色分别为文字,Button Down的背景颜色,Button Up的背景颜色
COLORREF m_TextColor, m_DownColor, m_UpColor;
//Button Down的背景颜色,Button Up的背景颜色标志位
bool bDownColor, bUpColor;
//设置文字大小
int nWordSize = 100;
UINT imageID;//记录图片ID
public:
//设置Button Down的背景颜色
void SetDownColor(COLORREF color);
//设置Button Up的背景颜色
void SetUpColor(COLORREF color);
//设置背景图片
void SetBkImage(UINT ID);
//设置文字大小,默认为10号
void SetWordSize(int wordsize);
//设置文字颜色默认为黑色
void SetWordColor(COLORREF wordColor);
// 文字处理
void SetWord(CRect rect, CDC* pDc, int size);
//绘制背景图片
void DrawBkImage(CRect rect, CDC* pDc, UINT ID);
};
#include "pch.h"
#include "MyButton.h"
void MyButton::SetDownColor(COLORREF color)
{
m_DownColor = color;
bDownColor = TRUE;//颜色是否设置标志位
}
void MyButton::SetUpColor(COLORREF color)
{
m_UpColor = color;
bUpColor = TRUE;
}
void MyButton::SetBkImage(UINT ID)
{
imageID = ID;
}
void MyButton::SetWordSize(int wordsize)
{
nWordSize = wordsize;
}
void MyButton::SetWordColor(COLORREF wordColor = RGB(255, 255, 255))
{
m_TextColor = wordColor;
}
void MyButton::DrawBkImage(CRect rect, CDC* pDc, UINT ID)
{
CDC dcBmp; //定义并创建一个内存设备环境
dcBmp.CreateCompatibleDC(pDc); //创建兼容性DC
CBitmap bmpBackground;
bmpBackground.LoadBitmap(ID); //载入资源中图片
BITMAP m_bitmap; //图片变量
bmpBackground.GetBitmap(&m_bitmap); //将图片载入位图中
//将位图选入临时内存设备环境
CBitmap* pbmpOld = dcBmp.SelectObject(&bmpBackground);
//调用函数显示图片StretchBlt显示形状可变
pDc->SetStretchBltMode(HALFTONE);
pDc->StretchBlt(rect.left, rect.top, rect.Width(), rect.Height(), &dcBmp, 0, 0, m_bitmap.bmWidth, m_bitmap.bmHeight, SRCCOPY);
}
// 文字处理
void MyButton::SetWord(CRect rect, CDC* pDc, int nWordSize)
{
CString str;
GetWindowText(str);//获取文字
CFont font;
font.CreatePointFont(nWordSize, L"仿宋");//设置控件文字大小与字体
pDc->SelectObject(&font);
pDc->SetTextColor(m_TextColor);//设置文字颜色
// dc.SetBkColor(RGB(255, 0, 0));//设置文字背景颜色
pDc->SetBkMode(TRANSPARENT);//设置文字背景透明
pDc->DrawText(str, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_TABSTOP);//绘制文字
}
void MyButton::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
CDC dc;
dc.Attach(lpDIS->hDC);//得到绘制的设备环境CDC
dc.SelectStockObject(NULL_BRUSH);//按钮为默认颜色
dc.Rectangle(&lpDIS->rcItem);//画出按钮框
CString str;
CRect rect = lpDIS->rcItem;
GetClientRect(&rect);//得到客户端坐标 //ScreenToClient();将屏幕坐标转换为客户端坐标
//加载背景图片,判断是否加载背景图片,没有则使用默认背景
if (imageID != 0) DrawBkImage(rect, &dc, imageID);
//按钮按下时改变背景色
if (lpDIS->itemState & ODS_SELECTED)
{
if (bDownColor)
{
CBrush brush(m_DownColor);
dc.FillRect(&(lpDIS->rcItem), &brush);//利用画刷brush,填充矩形框
}
}
else//当按钮不操作或者弹起时改变背景色
{
if (bUpColor)
{
CBrush brush(m_UpColor);
dc.FillRect(&(lpDIS->rcItem), &brush);//利用画刷brush,填充矩形框
}
}
//文字处理(绘制文字)
SetWord(rect, &dc, nWordSize);
dc.Detach();
}
效果
/*放在OnInitDialog函数或OnPaint函数里*/
m_button1.SetUpColor(RGB(255, 0, 0));
m_button1.SetDownColor(RGB(0, 0, 255));
如果要在按钮上显示图片,不要设置按钮按下和弹起的颜色,这会覆盖图片
void C按钮控件自绘Dlg::OnPaint()
{
CPaintDC dc(this);
CRect rect;
GetDlgItem(IDC_BUTTON1)->GetWindowRect(&rect);//获取控件的屏幕坐标
ScreenToClient(&rect);//转换为窗口的的客户区坐标
m_button1.SetBkImage(IDB_BITMAP1);
m_button1.DrawBkImage(rect, &dc, IDB_BITMAP1);
}
这个时候点击按钮并没有什么变化(这里指视觉上的变化,消息还是有的)
那么可以在DrawItem()函数里添加一些功能,比如按钮按下的时候绘制边框,弹起时销毁边框
if ((lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
{
//设置按钮边框颜色为蓝色
COLORREF color = RGB(0, 0, 255);
CBrush brush(color);
dc.FrameRect(&(lpDIS->rcItem), &brush);//用画刷brush,填充矩形边框
}
//控制的选中状态结束,去掉边框
if (!(lpDIS->itemState & ODS_SELECTED) && (lpDIS->itemAction & ODA_SELECT))
{
CBrush brush(m_UpColor);
dc.FrameRect(&lpDIS->rcItem, &brush);
}
LPDRAWITEMSTRUCT结构体