图形界面上的任意形状图形按钮

 
[ 原创文档 本文适合初级读者 已阅读19084次 ]

图形界面上的任意形状图形按钮
作者:effortsboysZYB

下载源代码

摘要

随着计算机的发展和普及,人们对于软件的界面美观性要求越来越高。MFC提供了很多标准控件,比如按钮控件,按钮在MFC编程中有着较高的使用频率。本文将介绍如何实现在有背景图片的情况下,任意形状按钮的自绘方法。本文使用基于对话框工程程序进行演示。

关键字: VC++ 按钮 自绘 任意形状 图形

一、实现原理

我们知道windows窗口默认都是矩形,要实现任意形状的窗口就需要自绘。为此从CBUTTON派生一个按钮类CControlButton,重载DrawItem消息处理进行自绘。图片的背景是矩形的,假如我们的按钮图片是圆形的,当把图片绘制上去之后,我们发现多出了背景部分。如何消除背景呢?

为了解决这个问题,我们可以用BitBlt 中的MERGEPAINT和SRCAND的方式进行绘制。 MERGEPAINT是把图形反色后再同贴图目的地进行OR操作,而SRCAND是把图形和贴图目的地进行AND操作。在计算机中,使用的是数字图像处理,每一种颜色都是由RGB表示的,RGB是指红、绿、蓝三原色,只要有这3种颜色和对应的颜色强度就可以合成各种颜色了。比如,黑色的RGB值为(0,0,0),白色的RGB值为(255,255,255),括号内对应的是红绿蓝3种颜色的强度。在数字图像处理中可以实现OR、AND等逻辑运算。任何颜色同白色进行OR运算结果都为白色,进行AND运算结果都是该颜色本身;任何颜色跟黑色进行OR运算结果都为该颜色本身;进行AND运算结果都是黑色。为此,我们准备两张图片,如下图所示:


图1        图2

图1的背景为白色,图2是将图1中需要显示部分填充黑色而得。实现去除背景贴图关键代码如下:

if (IsMask==TRUE) //值为真则去除图片背景
{
       CDC MaskDC;
       MaskDC.CreateCompatibleDC(pDC);
       if (IsBackBmp==TRUE)//使用和主窗口相同的背景图片
       {
              CBitmap *pOldBmp;
              CDC BackDC;
              BackDC.CreateCompatibleDC(pDC);
              pOldBmp = MaskDC.SelectObject(&m_MaskBitmap);
              BackDC.SelectObject(&m_BackBitmap);
              pDC->BitBlt(0,0,rect.Width(),rect.Height(),&BackDC,BackRect.left,BackRect.top,SRCCOPY);
       }

       pDC->BitBlt(0,0,rect.Width(),rect.Height(),&MaskDC,0,0,MERGEPAINT);
       pDC->BitBlt(0,0,rect.Width(),rect.Height(),&MemDC,0,0,SRCAND);
       ReleaseDC(&MaskDC);
       }

       else
       {
              pDC->BitBlt(0,0,rect.Width(),rect.Height(),&MemDC,0,0,SRCCOPY);
       }

MaskDC是图2的DC,MemDC为图1的DC。
效果如下图所示:


可能这时你就纳闷了,为什么背景色还是白色呢,是不是代码没有去掉图片的背景色呢?答案是贴图的时候已经去掉了背景色。请看分析

按钮是一个子窗口,默认情况下主窗口和按钮子窗口背景都是白色,但是往往我们需要在主窗口上绘制一张图片,这样窗口看起来就比较美观。这样子做之后,按钮子窗口和主窗口的背景就不一样了。在进行按钮自绘的时候,那就是把按钮背景作为目的地进行OR、AND运算,因为按钮背景就是白色的,所以效果看起来也就是白色的。

要解决这个问题也很简单,我们获取按钮所在主窗口中的矩形区域,把这个区域的主窗口背景绘制到按钮中,再进行绘制按钮图片的操作就可以了。

通过这样做之后,效果如下图:


 

为此,我们已经得到一个图片按钮了。但仅仅这样还不行,这按钮的响应区域还是矩形区域,也就是说除了按钮图片之外的区域也响应鼠标点击。那我们就需要构造一个按钮图片区域,使用库函数SetWindowRgn就可以确定响应区域了。SetWindowRgn有个参数为HRGN类型,因此我们需要获得一个HRGN。

Jean-Edouard Lachand-Robert 写了一个 BitmapToRegion 函数,函数的功能为把一张位图根据一种颜色转化为一个区域,这个我们就可以得到一个HRGN。有关BitmapToRegion详情请看代码说明。我们用图2中的黑色区域去转化成区域,为此我们就得了一个图片按钮的响应区域了。

另外,CControlButton类还提供了通常的四态按钮的支持,即鼠标划过、点击、正常、获得焦点四种情况对应加载四张不同的位图。

 

(另注:还有另一种简单方法:

添加 =WM_CTLCOLOR 消息响应,注意前面有等号。

HBRUSH CRgnBmpBtn::CtlColor(CDC* pDC, UINT nCtlColor) 
{
// TODO: Change any attributes of the DC here

// TODO: Return a non-NULL brush if the parent's handler should not be called
return (HBRUSH)GetStockObject(NULL_BRUSH);
}

改成这样就可以了。 ( catlause 发表于 2010-3-2 14:28:00))

二、成员函数介绍

① void CControlButton::SetMaskBitmapId(int mask, bool action)

功能:设置图2资源图片

返回值:无

参数:mask ,图2的资源ID

action,值为TRUE则有效,FALSE为无效

② void CControlButton::SetBackBmp(int nBgdBmpId,CRect rect);

功能:设置按钮背景图片

返回值:无

参数:nBgdBmpId ,主窗口背景图片资源ID

rect , 按钮在主窗口中的客户区矩形, 使用GetWindowRect, ScreenToClient这两个函数即可以轻松获得。

③ void CControlButton::SetRgnMask(int nMaskBmpId, bool nAction)

功能:设置有效区域函数:

返回值:无

参数:nMaskBmpId ,图2的资源ID

nAction ,值为TRUE则设置有效,FALSE则无效,通过这样可以使用或禁止构造响应区域

④void CControlButton::SetBitmapId(int nOver,int nNormal,int nPressed,int nFocus)

功能:设置按钮动态加载的四幅图片 :

返回值:无

参数:nOver,鼠标划过对应按钮图片资源ID。
nNormal ,正常状态下 对应按钮图片资源ID
nPressed ,按下按钮对应图片资源ID
nFocus ,获得焦点情况下图片资源ID

三、使用说明

CControlButton类从CButton类派生,使用时,只需在界面上放置一个按钮控件,添加CControlButton类,关联一个CControlButton的控件变量,然后进行初始化即可:

CRect btnRect; //定义按钮矩形变量
m_demoBtn.GetWindowRect(btnRect); //获取按钮窗口矩形区域
ScreenToClient(btnRect); //转换成客户区域

//设置按钮的背景图片,跟主窗口的背景图片一样
m_demoBtn.SetBackBmp(IDB_BACKGROUND,btnRect);
m_demoBtn.SetRgnMask(IDB_OKmask,TRUE);//设置响应区域,TRUE设置构造区域有效
m_demoBtn.SetMaskBitmapId(IDB_OKmask,TRUE);   //设置掩码图片

//设置按钮的四种状态图
m_demoBtn.SetBitmapId(IDB_btn_ok_b,IDB_btn_ok_a,IDB_btn_ok_c,IDB_btn_ok_a); 

四、结束语

本类是在我朋友hurryboylqs四态图片按钮类的基础上完成,衷心感谢hurryboylqs的帮助,希望本文对大家有一点点帮助。本人深知本类还有一些不足之处,如若大家对本类有修改完善,也请连修改说明给我发送一份,万分感谢!

Email:qvb20974151@163.com

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值