说明
这部分的功能参照了Qt的部分自绘控件进行的相关设计,可以很大程度上的提升按钮的显示效果。主要是通过渐变色和颜色雾化处理的方法进行的操作,经过测试,该方法使用在圆形按钮上会有不错的效果,但是用在方形按钮上就会难看的要死。
效果展示
正常状态下的按钮
按钮按下后的效果
程序
#pragma once
// CRoundButton
class CRoundButton : public CButton
{
DECLARE_DYNAMIC(CRoundButton)
public:
CRoundButton();
virtual ~CRoundButton();
protected:
DECLARE_MESSAGE_MAP()
public:
virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/);
virtual void PreSubclassWindow();
virtual void DrawOutRing(CDC* pDC, CRect &rct); //绘制外环
virtual void DrawInRing(CDC* pDC, CRect &rct); //绘制内环
virtual void DrawInterArea(CDC* pDC, CRect &rct); //绘制内部区域
virtual void DrawFogArea(CDC* pDC, CRect &rct); //雾化部分区域
private:
COLORREF ringBrush1, ringBrush2; //环的两个颜色
COLORREF m_normalBrush, m_activeBrush; //正常颜色, 按下颜色
bool pushState; //按下状态
};
//CRoundButton.h
include "pch.h"
#include "MFCApplication9.h"
#include "CRoundButton.h"
// CRoundButton
IMPLEMENT_DYNAMIC(CRoundButton, CButton)
CRoundButton::CRoundButton()
{
m_normalBrush = RGB(120, 120, 255);
m_activeBrush = RGB(255, 120, 120);
ringBrush1 = RGB(255, 255, 255);
ringBrush2 = RGB(125, 125, 125);
pushState = false;
}
CRoundButton::~CRoundButton()
{
}
BEGIN_MESSAGE_MAP(CRoundButton, CButton)
END_MESSAGE_MAP()
// CRoundButton 消息处理程序
void CRoundButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
// TODO: 添加您的代码以绘制指定项
pushState = false;
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
int nSaveDC = pDC->SaveDC();//存储当前设备环境,以便绘图结束时恢复原来状态</span>
CPen m_Pen(PS_SOLID,1,RGB(0,0,0));
pDC->SelectObject(&m_Pen);//选择画笔
CRect rct = lpDrawItemStruct->rcItem;//获取按钮矩形区域
if (lpDrawItemStruct->itemState&ODS_SELECTED)//绘制按钮按下时的颜色
{
pushState = true;
}
DrawOutRing(pDC, rct);
DrawInRing(pDC, rct);
DrawInterArea(pDC, rct);
DrawFogArea(pDC, rct);
//pDC->Ellipse(&rct);//画椭圆按钮,这一步用了之前选择的画笔和画刷
重绘字体
//pDC->SetBkMode(TRANSPARENT);//重绘文本时不擦除背景即透明模式,如果选择OPAQUE(不透明),在文本四周有白色矩形边框,十分之难看
CString strText{};//c++11版本以下不支持此方法
GetWindowText(strText);//获取按钮文本
pDC->SetBkMode(TRANSPARENT);
pDC->DrawText(strText, rct, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//重绘按钮文本
//恢复设备环境
pDC->RestoreDC(nSaveDC);
}
void CRoundButton::PreSubclassWindow()
{
// TODO: 在此添加专用代码和/或调用基类
ModifyStyle(0, BS_OWNERDRAW);//改为自绘风格
CRgn rgn;
CRect rct;
GetClientRect(&rct);
rgn.CreateEllipticRgnIndirect(&rct);//在按钮矩形内创建椭圆区域
::SetWindowRgn(GetSafeHwnd(), (HRGN)rgn, true);//将椭圆区域应用到按钮上
CButton::PreSubclassWindow();
}
void CRoundButton::DrawOutRing(CDC * pDC, CRect & rct) //绘制外环
{
int nSaveDC = pDC->SaveDC();//存储当前设备环境,以便绘图结束时恢复原来状态</span>
CDC dcBAK;
CBitmap bitmap;
dcBAK.CreateCompatibleDC(pDC);
bitmap.CreateCompatibleBitmap(pDC, rct.Width(), rct.Height());
CBitmap*poldbmp = dcBAK.SelectObject(&bitmap);
COLORREF color1, color2;
color1 = ringBrush1;
color2 = ringBrush2;
int r1 = color1 & 255, g1 = color1 >> 8 & 255, b1 = color1 >> 16 & 255;
int r2 = color2 & 255, g2 = color2 >> 8 & 255, b2 = color2 >> 16 & 255;
for (int i = 0; i < rct.Height(); i++)
{
int r, g, b;
r = r1 + (i * (r2 - r1) / rct.Height());
g = g1 + (i * (g2 - g1) / rct.Height());
b = b1 + (i * (b2 - b1) / rct.Height());
dcBAK.FillSolidRect(0, i, rct.Height(), 1, RGB(r, g, b));
}
CRgn rgn;
rgn.CreateEllipticRgn(rct.left, rct.top, rct.right, rct.bottom);
CBrush brush;
brush.CreatePatternBrush(&bitmap);
pDC->FillRgn(&rgn, &brush);
//恢复设备环境
pDC->RestoreDC(nSaveDC);
}
void CRoundButton::DrawInRing(CDC * pDC, CRect & rct)
{
int nSaveDC = pDC->SaveDC();//存储当前设备环境,以便绘图结束时恢复原来状态</span>
CDC dcBAK;
CBitmap bitmap;
dcBAK.CreateCompatibleDC(pDC);
bitmap.CreateCompatibleBitmap(pDC, rct.Width(), rct.Height());
CBitmap*poldbmp = dcBAK.SelectObject(&bitmap);
COLORREF color1, color2;
color1 = ringBrush2;
color2 = ringBrush1;
int r1 = color1 & 255, g1 = color1 >> 8 & 255, b1 = color1 >> 16 & 255;
int r2 = color2 & 255, g2 = color2 >> 8 & 255, b2 = color2 >> 16 & 255;
for (int i = 0; i < rct.Height(); i++)
{
int r, g, b;
r = r1 + (i * (r2 - r1) / rct.Height());
g = g1 + (i * (g2 - g1) / rct.Height());
b = b1 + (i * (b2 - b1) / rct.Height());
dcBAK.FillSolidRect(0, i, rct.Height(), 1, RGB(r, g, b));
}
CRect rct1;
POINT point1, point2;
point1.x = rct.left + rct.Width()*0.05;
point1.y = rct.top + rct.Height()*0.05;
point2.x = rct.right - rct.Width()*0.05;
point2.y = rct.bottom - rct.Height()*0.05;
rct1.SetRect(point1, point2);
CRgn rgn;
rgn.CreateEllipticRgn(rct1.left, rct1.top, rct1.right, rct1.bottom);
CBrush brush;
brush.CreatePatternBrush(&bitmap);
pDC->FillRgn(&rgn, &brush);
//恢复设备环境
pDC->RestoreDC(nSaveDC);
}
void CRoundButton::DrawInterArea(CDC * pDC, CRect & rct)
{
int nSaveDC = pDC->SaveDC();//存储当前设备环境,以便绘图结束时恢复原来状态</span>
CBrush brush;
if (pushState)
brush.CreateSolidBrush(m_activeBrush);
else
brush.CreateSolidBrush(m_normalBrush);
//brush.CreateSolidBrush(RGB(122, 122, 255));
CRect rct1;
POINT point1, point2;
point1.x = rct.left + rct.Width()*0.1;
point1.y = rct.top + rct.Height()*0.1;
point2.x = rct.right - rct.Width()*0.1;
point2.y = rct.bottom - rct.Height()*0.1;
rct1.SetRect(point1, point2);
CRgn rgn;
rgn.CreateEllipticRgn(rct1.left, rct1.top, rct1.right, rct1.bottom);
pDC->FillRgn(&rgn, &brush);
//恢复设备环境
pDC->RestoreDC(nSaveDC);
}
void CRoundButton::DrawFogArea(CDC * pDC, CRect & rct)
{
int nSaveDC = pDC->SaveDC();//存储当前设备环境,以便绘图结束时恢复原来状态</span>
CBrush brush;
COLORREF color;
if (pushState)
color = m_activeBrush;
else
color = m_normalBrush;
int r = color & 255, g = color >> 8 & 255, b = color >> 16 & 255;
r = r + 10 > 255 ? 255 : r + 10;
g = g + 10 > 255 ? 255 : g + 10;
b = b + 10 > 255 ? 255 : b + 10;
brush.CreateSolidBrush(RGB(r,g,b));
CRect rct1;
POINT point1, point2;
point1.x = rct.left + rct.Width()*0.1;
point1.y = rct.top + rct.Height()*0.1;
point2.x = rct.right - rct.Width()*0.1;
point2.y = rct.bottom - rct.Height()*0.1;
rct1.SetRect(point1, point2);
CRect rct2;
POINT point3, point4;
point3.x = rct.left + rct.Width()*0.1;
point3.y = rct.top + rct.Height()*0.1;
point4.x = (rct.right - rct.Width()*0.1)*2;
point4.y = (rct.bottom - rct.Height()*0.1)*2.5;
rct2.SetRect(point3, point4);
CRgn rgn, rgn2;
rgn2.CreateEllipticRgn(rct2.left, rct2.top, rct2.right, rct2.bottom);
rgn.CreateEllipticRgn(rct1.left, rct1.top, rct1.right, rct1.bottom);
rgn.CombineRgn(&rgn, &rgn2, RGN_DIFF);
pDC->FillRgn(&rgn, &brush);
//恢复设备环境
pDC->RestoreDC(nSaveDC);
}
最后
目前的程序还比较简陋,使用的颜色的信息都是程序初始化给的颜色,如果需要自定义颜色,可以添加设置颜色的相关程序,一共设置四个颜色即可,其中外环的两个颜色不要做过大的改动,改动过大会影响显示的效果。