MFC自绘圆形按钮

MFC自绘圆形按钮

说明

这部分的功能参照了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);
}

最后

目前的程序还比较简陋,使用的颜色的信息都是程序初始化给的颜色,如果需要自定义颜色,可以添加设置颜色的相关程序,一共设置四个颜色即可,其中外环的两个颜色不要做过大的改动,改动过大会影响显示的效果。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MFC 中创建圆角矩形按钮,可以使用 `CButton` 类的派生类,自定义绘制函数来实现。下面是一份示例代码: 1. 创建一个自定义按钮类 `CRoundRectButton` ```c++ class CRoundRectButton : public CButton { public: CRoundRectButton(); virtual ~CRoundRectButton(); protected: virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct); }; ``` 2. 在 `CRoundRectButton` 类中实现 `DrawItem` 函数,用于绘制圆角矩形按钮 ```c++ void CRoundRectButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); CRect rect = lpDrawItemStruct->rcItem; if (lpDrawItemStruct->itemState & ODS_SELECTED) // 按下状态 { pDC->FillSolidRect(rect, ::GetSysColor(COLOR_BTNFACE)); pDC->DrawEdge(rect, EDGE_SUNKEN, BF_RECT); } else // 正常状态 { pDC->FillSolidRect(rect, ::GetSysColor(COLOR_BTNFACE)); pDC->DrawEdge(rect, EDGE_RAISED, BF_RECT); } // 绘制圆角矩形 int nRound = 10; // 圆角大小 CRect roundRect(rect.left + 2, rect.top + 2, rect.right - 2, rect.bottom - 2); pDC->RoundRect(roundRect, CPoint(nRound, nRound)); } ``` 3. 在对话框类中添加 `CRoundRectButton` 控件,并设置样式 ```c++ CRoundRectButton m_btnRound; m_btnRound.Create(_T("圆角按钮"), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, CRect(50, 50, 150, 80), this, IDC_BTN_ROUNDRECT); ``` 在上面的代码中,`m_btnRound` 是一个 `CRoundRectButton` 类的对象,通过 `Create` 函数创建一个圆角矩形按钮,并设置样式为 `WS_CHILD | WS_VISIBLE | BS_OWNERDRAW`,其中 `BS_OWNERDRAW` 表示该按钮使用自定义绘制函数。 运行程序后即可看到一个圆角矩形按钮

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值