MFC 圆形按钮

参考网络一些大佬的资料,整理了篇学习记录,保存一下!
实现按钮自绘.界面中已有按钮控件,我们修改它的形状。
在这里插入图片描述

步骤:
1.创建基于对话框的MFC项目,命名”RoundButtonDemo“,此处不啰嗦;
2.先在窗口设计视图中添一个按钮控件,这里不修改它的属性,采用默认。 如下图
在这里插入图片描述

3.打开类向导添加一个类CRoundButton,基类为CButton,新添加的类只有一个构造函数和析构函数
在这里插入图片描述

4.在类向导中为CRoundButton类添加两个虚函数DrawItem()和PreSubclassWindow();这两个函数的作用在下面有简单说明。
在这里插入图片描述

5.PreSubclassWindow()该函数可以初始化子类窗口,做一些绘制子类窗口之前要做的事情,如按钮风格的修改,按钮形状的修改。
这里我们修改按钮的风格,改为ODS_OWNERDRAW风格,即自绘风格,这点很关键,不然什么效果都没有。当然如果这里不修改按钮自绘风格,一定要在按钮控件属性中将Owner Draw 设置为True;
还有在原理部分我们说过要切掉多余的四个边角,也在这里进行。下面是代码:
.
void CRoundButton::PreSubclassWindow()
{
// TODO: 在此添加专用代码和/或调用基类
ModifyStyle(0, BS_OWNERDRAW);//改为自绘风格
// 绘制按钮可用区域,切掉四个边角
CRgn rgn;
CRect rct;
GetClientRect(&rct);
rgn.CreateEllipticRgnIndirect(&rct);//在按钮矩形内创建椭圆区域
::SetWindowRgn(GetSafeHwnd(), (HRGN)rgn, true);//将椭圆区域应用到按钮上
CButton::PreSubclassWindow();
}

6.上一步我们设置了按钮的可用区域,这一步我们就要绘制按钮,包括按钮边框,按钮在不同状态下的颜色,简单起见,我们只绘制按钮正常状态和按下状态下的颜色。
这些事情我们在DrawItem()函数中完成。先看下代码,再来分析。
.
void CRoundButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)//记得要把/lpDrawItemStruct/的注释去掉
{
// TODO: 添加您的代码以绘制指定项
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
int nSaveDC = pDC->SaveDC();//存储当前设备环境,以便绘图结束时恢复原来状态
pDC->SelectObject(&m_normalBrush);//选择按钮正常状态(默认状态)下的画刷
pDC->SelectObject(&m_Pen);//选择画笔
CRect rct = lpDrawItemStruct->rcItem;//获取按钮矩形区域
if (lpDrawItemStruct->itemState&ODS_SELECTED)//绘制按钮按下时的颜色
{
pDC->SelectObject(&m_activeBrush);
}
pDC->Ellipse(&rct);//画椭圆按钮,这一步用了之前选择的画笔和画刷
//重绘字体
pDC->SetBkMode(TRANSPARENT);//重绘文本时不擦除背景即透明模式,如果选择OPAQUE(不透明),在文本四周有白色矩形边框,十分之难看
CString strText{};//c++11版本以下不支持此方法
GetWindowText(strText);//获取按钮文本
pDC->DrawText(strText, rct, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//重绘按钮文本
//恢复设备环境
pDC->RestoreDC(nSaveDC);
}

现在一句句来分析
(1) CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
int nSaveDC = pDC->SaveDC();

这里有一个CDC类,这个类就是用来绘图的。CDC每一个C是C++类名前缀,DC是 Device Context的缩写。 DC一般译为设备环境或设备上下文,似乎有些难以理解。
举个例子,你开了汽车修理店,同时在旁边屋里又搞手机修理。现在你正拿着大扳手修汽车,你周边的所有都是设备环境DC,如地上有些机油的车库,你的扳手,还有你修理的车子,除了你自己之外都是设备环境,甚至干活的你都可算做是设备环境。这时有人来找你修手机,那么你得把扳手放好,把车库门关好,免得有不明情况的人来弄坏现场,偷东西。那么修手机的地方又是另一个设备环境,桌子,台灯,焊枪,镊子,小螺丝刀……都是修手机的设备环境。修完手机,我们回来继续修车子,那得要打开车库,拿出扳手,继续干活。
现在我们说回CDC类,画图要画板,画笔,画刷,颜料等,这些属于绘图的环境。假如现在我们完成了主窗体的绘制,现在要绘制按钮(画笔画边框,画刷给按钮图色),那我们要先把绘制主窗体的设备环境保存起来,因为绘制按钮我们会使用不同的画笔(CPEN)和画刷(CBRUSH)。
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);获取当前窗口(主窗体)的设备环境。
int nSaveDC = pDC->SaveDC(); 保存当前设备环境,完成按钮绘制后,要恢复这个环境。
(2)pDC->SelectObject(&m_normalBrush); 选择画刷,涂颜色
pDC->SelectObject(&m_Pen);选择 画笔,画边框
SelectObject()两个参数m_normalBrush ,m_Pen和稍后用到的m_activeBrush是CRoundButton的成员变量,因以我们要先添加这些变量。打开类向导,添加自定义成员变量(创建变量时选择“私有”),如下图
在这里插入图片描述

#pragma once
#include “afxwin.h”
class CRoundButton :
public CButton
{
public:
CRoundButton();
~CRoundButton();
virtual void PreSubclassWindow();
virtual void DrawItem(LPDRAWITEMSTRUCT /lpDrawItemStruct/);
private:
CBrush m_normalBrush;
CPen m_Pen;
CBrush m_activeBrush;
};
在RoundButton.cpp文件中添加构造函数的代码初始化这三个变量,并在析构函数中删除。代码如下:
CRoundButton::CRoundButton()
{
m_Pen.CreatePen(PS_SOLID, 1, RGB(201, 201, 233));
m_normalBrush.CreateSolidBrush(RGB(231, 221, 223));//正常状态下的按钮颜色
m_activeBrush.CreateSolidBrush(RGB(201, 201, 233));//按钮按下时的按钮颜色
}
CRoundButton::~CRoundButton()
{
m_Pen.DeleteObject();
m_normalBrush.DeleteObject();
m_activeBrush.DeleteObject();
}

我们接着说,SelectObject()函数是CDC的成员函数,选择画图所用的画笔,画刷,字体。用这个函数选择了画刷画笔后,之后的画图就会应用这些绘画工具。

(3)CRect rct = lpDrawItemStruct->rcItem;
if (lpDrawItemStruct->itemState&ODS_SELECTED)
{
pDC->SelectObject(&m_activeBrush);
}
pDC->Ellipse(&rct);
这一段代码有详细注释,此处就不解释了,大家可以了解下“DRAWITEMSTRUCT”相关知识,以便能够更好地理解。
(4)pDC->SetBkMode(TRANSPARENT);//重绘文本时不擦除背景,即透明模式,如果选择OPAQUE(不透明),在文本四周有白色矩形边框,十分之难看
CString strText{};//采用列表方式初始化文本对象,c++11版本以下不支持此方法
GetWindowText(strText);//获取按钮文本
pDC->DrawText(strText, rct, DT_CENTER | DT_VCENTER | DT_SINGLELINE);//重绘按钮文本
DT_CENTER:指定文本水平居中显示。
DT_VCENTER:指定文本垂直居中显示。该标记只在单行文本输出时有效,所以它必须与DT_SINGLELINE结合使用。
DT_SINGLELINE:单行显示文本,回车和换行符都不断行。
(5)pDC->RestoreDC(nSaveDC);最后 一步是恢复设备环境。

7.通过前面的工作,我们建立了CRoundButton类,现在我们就要将这个类应用到我在步骤2添加的按钮上。打开主窗体设计视图,右击按钮选择“添加变量”菜单项,弹出对话框,添加m_btn变量:
在这里插入图片描述

这样我在主窗体类中定义了m_btn,它的类型是CRoundButton,关联的控件ID为IDC_BUTTON1(即我们步骤2添加的按钮)。
(8)现在我们可以编译了。执行结果如下:
在这里插入图片描述
在这里插入图片描述

  • 31
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值