分享
 
 
 

在VC中透明浮动按键的实现

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

有一种按键,看起来是一幅完整的图片,当鼠标移到按键区域时,图片的一部分凸现,形成一个按键,当鼠标移走时又恢复原来状态。

最近,看了一些关于浮动按键的代码,其原理大致上跟CBitmapButton差不多,用数幅位图代表按键的各个状态,响应鼠标的各种消息来设置按键的状态,实现按键的浮动显示,但是这样的按键却不能和周围的背景混和成一幅图片。

为了实现“透明”按键,可以简单地做个试验:先在对话框中加入一个BUTTON,通过属性框选“Owner Draw”风格,再加入一个PICTURE,并加入图片,将BUTTON移到PICTURE上。运行结果发现,按键没有显示出来,但在按键区域按下鼠标时,该按键仍然能发出WM_COMMAND消息,这样一个纯透明的按键建立了。显然,这个按键是毫无使用意义的,因为用户不知道按键的位置,必须让用户容易觉察到按键的位置,可以把这个按键改造一下:

(首先从CButton派生出一个新类CDrawButton)

·把按键的标题显示出来

这个实现起来比较简单,我们可以重载CButton类的成员函数DrawItem(),

void CDrawButton::DrawItem

(LPDRAWITEMSTRUCT lpDrawItemStruct)

{

CDC dc;

CRect rect=lpDrawItemStruct->rcItem;//得到按键区域

CString sCaption;

dc.Attach(lpDrawItemStruct- >hDC); //得到设备环境CDC

VERIFY(lpDrawItemStruct- >CtlType==ODT_BUTTON);

GetWindowText(sCaption);//得到按键的标题

dc.SetBkMode(TRANSPARENT);//透明显示

CFont* m_pOldFont=dc.SelectObject(m_pFont);

dc.DrawText(sCaption,&rect,DT_CENTER|DT_VCENTER|DT_SINGLELINE);

dc.SelectObject(m_pOldFont);

}

其中的m_pFont是成员变量,它保存了对话框的字体指针,为了按键的标题风格与对话框的字体风格一致,在初始化时调用对话框的成员函数GetFont()即可得到指向对话框字体的CFont类指针。

·使按键浮动显示

要通过自绘来表示按键的各种状态,可填写DRAWITEMSTRUCT来通知DrawItem()函数需要做什么,我们先了解一下DRAWITEMSTRUCT:

typedef struct tagDRAWITEMSTRUCT{

UINT CtlType; // 控件类型

UINT CtlID;// 控件的ID号

UNIT itemID;//菜单项的索引

UINT itemAction;// 绘图操作

UINT itemState; // 状态

HWND hwndItem; // 控件的窗口句柄

HDC hDC; // 相关的设备环境

RECT rcItem;//控件的范围

DWORD itemData;//指定与菜单项相联系的应用程序定义的32位值

}DRAWITEMSTRUCT;

利用这个结构先做一个按键状态设置函数:

void CDrawButton::SetButtonMode(UINT action, UINT mode)

{

// TODO: Add your message handler code

here and/or call default

DRAWITEMSTRUCT DIS;

DIS.CtlType = ODT_BUTTON;

DIS.CtlID = GetDlgCtrlID();

DIS.itemAction = action;

DIS.itemState = mode;

DIS.hwndItem = GetSafeHwnd();

DIS.hDC = GetDC()- >GetSafeHdc();

GetClientRect(&(DIS.rcItem));

SendMessage(WM_DRAWITEM,(WPARAM)

GetSafeHwnd(),(LPARAM)&DIS);

ReleaseDC(CDC::FromHandle(DIS.hDC));

}

这样,我们可以响应鼠标的各种消息来设置按键的各种状态:

void CDrawButton::OnMouseMove

(UINT nFlags, CPoint point)

{

// TODO: Add your message handler code

here and/or call default

CRect rect;

GetClientRect(&rect);

if(rect.PtInRect(point)){

if (mBtnStats==BTN_NORMAL){

SetButtonMode(ODA_SELECT, ODS_FOCUS);

SetCapture();

}

}

else{

//AutoLoad(GetDlgCtrlID(),GetParent());

SetButtonMode(ODA_DRAWENTIRE,ODS_DEFAULT);

ReleaseCapture();

}

CButton::OnMouseMove(nFlags, point);

}

这里,mBtnStats是个UINT类型的成员变量,它可以有三种自定义状态:

BTN_NORMAL 正常状态

BTN_UP 鼠标移入按键区域或释放鼠标

BTN_DOWN 按下鼠标

(可以再加一种DISABLE状态)

当在按键区域释放鼠标时,必须发送WM_COMMAND消息:

void CDrawButton::OnLButtonUp(UINT nFlags, CPoint point)

{

// TODO: Add your message handler code

here and/or call default

CRect rect;

GetClientRect(&rect);

if(rect.PtInRect(point)){

if (mBtnStats==BTN_DOWN)

GetParent()- >SendMessage(WM_COMMAND,

MAKELPARAM(GetDlgCtrlID(),BN_CLICKED),

(LPARAM)GetSafeHwnd());

SetCapture();

}

else{

SetButtonMode(ODA_DRAWENTIRE,ODS_DEFAULT);

ReleaseCapture();

}

CButton::OnLButtonUp(nFlags, point);

}

接着就是绘制按键的各种状态:由于按键必须“透明”,所以在按下和释放时只在按键区域的四周加上一个3D边框就行了。而在正常状态下,则必须去掉边框恢复背景。但如何恢复背景图象呢?我是这样做的:在按键初始化时,先把被按键覆盖了的区域保存在一个CBitmap类中,以后需要重绘按键时就把这个CBitmap画在按键上就行了。

void CDrawButton::DrawItem

(LPDRAWITEMSTRUCT lpDrawItemStruct)

{

// TODO: Add your code to draw the specified item

CDC dc;

CRect rect=lpDrawItemStruct- >rcItem;

CString sCaption;

dc.Attach(lpDrawItemStruct->hDC);

//得到绘制的设备环境CDC

VERIFY(lpDrawItemStruct- >CtlType==ODT_BUTTON);

if (lpDrawItemStruct- >itemAction & ODA_DRAWENTIRE){

//重绘控件(正常状态)

mBtnStats=BTN_NORMAL;

if (m_pBitmap!=0){

CDC memDC;

memDC.CreateCompatibleDC(&dc);

memDC.SelectObject(m_pBitmap);

dc.BitBlt(0, 0, rect.Width(), rect.Height(),

&memDC, 0, 0, SRCCOPY);

memDC.DeleteDC();

}

//显示按键标题

GetWindowText(sCaption);

dc.SetBkMode(TRANSPARENT);

if (m_pFont!=0){

CFont* m_pOldFont=dc.SelectObject(m_pFont);

dc.DrawText(sCaption,&rect,

DT_CENTER|DT_VCENTER|DT_SINGLELINE);

dc.SelectObject(m_pOldFont);

}

}

if ((lpDrawItemStruct- >itemState & ODS_SELECTED) &&

(lpDrawItemStruct- >itemAction & ODA_SELECT)){

//按下鼠标

mBtnStats=BTN_DOWN;

dc.Draw3dRect(&rect,RGB(128,128,128),RGB(192,192,192));

rect.top=rect.top+1;rect.bottom=rect.bottom-1;

rect.left=rect.left+1;rect.right=rect.right-1;

dc.Draw3dRect(&rect,RGB(0,0,0),RGB(255,255,255));

}

if(!(lpDrawItemStruct- >itemState & ODS_SELECTED) &&

(lpDrawItemStruct- >itemAction & ODA_SELECT)){

//释放鼠标或鼠标进入按键区域

mBtnStats=BTN_UP;

dc.Draw3dRect(&rect,RGB(255,255,255),RGB(0,0,0));

rect.top=rect.top+1;rect.bottom=rect.bottom-1;

rect.left=rect.left+1;rect.right=rect.right-1;

dc.Draw3dRect(&rect,RGB(192,192,192),RGB(128,128,128));

}

dc.Detach();

}

接着就必须一些初始化工作,其中最关键就是把被按键覆盖了的区域保存进CBitmap类中,我们知道CDC::StretchBlt()函数可以把位图的指定区域从一个设备拷贝到另一个设备中,这样可以很方便地把窗口或对话框的某个区域保存,条件是获得其DC:

void CDrawButton::LoadBack(CWnd *pParent)

{

ASSERT(GetStyle() & BS_OWNERDRAW);

if (m_pBitmap!=0) return;

CRect rect;

GetWindowRect(&rect);

pParent- >ScreenToClient(&rect);//获得按键区域

CPaintDC dc(pParent);

if (m_pBitmap==0) m_pBitmap=new CBitmap;//初始化位图

m_pBitmap- >CreateCompatibleBitmap

(&dc,rect.Width(),rect.Height());

CDC memDC;

memDC.CreateCompatibleDC(&dc);

memDC.SelectObject(m_pBitmap);

memDC.StretchBlt(0, 0, rect.Width(),rect.Height(), &dc,

rect.left, rect.top,

rect.Width(),rect.Height(), SRCCOPY);//保存

memDC.DeleteDC();

m_pFont=pParent- >GetFont();//获得窗口或对话框的字体

ModifyStyle(0,WS_VISIBLE);//显示按键

SetBitmapMode(ODA_DRAWENTIRE,0);//绘制按键

}

而使这个类和对话框上的按键产生联系还必须调用一下SubclassDlgItem():

BOOL CDrawButton::AutoLoad(UINT nID, CWnd *pParent)

{

// first attach the CDrawButton to the dialog control

if (m_pBitmap!=0) return FALSE;

if (!SubclassDlgItem(nID, pParent)) return FALSE;

LoadBack(pParent);

return TRUE;

}

这个类还必须具有三个成员变量:

CFont* m_pFont;

CBitmap* m_pBitmap;

UINT mBtnStats;

在构造函数中初始化这些变量

m_pBitmap=0;

m_pFont=0;

//赋予0是可以的

mBtnStats=BTN_NORMAL;

在折构函数中拆除位图

if(m_pBitmap!=0) delete m_pBitmap;

这样,一个透明的浮动式按键类就做好了,具体实现方法以下:

1.接管对话框的BUTTON,首先在对话框上画一个BUTTON,再加一个PICTURE图片,BUTTON的风格必须加入OWNER DRAW及去掉VISIBLE,把BUTTON移到PICTURE上适当的位置,在对话框类加入CDrawButton类成员m_myButton,由于按键初始化时必须保存对话框的图象,而对话框在运行InitDialog()或第一次运行OnPaint()时对话框的控件还没有真正显示出来,我们只好在OnMouseMove()中进行初始化:

m_myButton.AutoLoad(IDC_BUTTON1,this);

AutoLoad()只运行一次。

2.动态建立CDrawButton,在对话框类或CxxxView类加入CDrawButton类成员m_myButton,可以在对话框的InitDialog()或CxxxView类的InitialUpdate()中加入:m_myButton.Create()函数,必须包含BS_OWNERDRAW而不能有WS_VISIBLE风格,然后在OnMouseMove()或OnDraw()中进行初始化:m_myButton.LoadBack(this);注意应加在OnDraw()的最后。

同样地,LoadBack()只运行一次。

(如果按键比背景的图片迟建立而具有可见(Visible)属性,则会把图片抹掉,所以必须去掉VISIBLE属性或不能加入WS_VISIBLE风格)

·当鼠标移到按键区域时,改变鼠标

这个很容易实现,不在这里多说了。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有