按钮的透明效果(看过CButtonST的最好)

j8daxue 2008-09-06 02:34:33
试着做个让背景透明的按钮,参考了CButtonST的代码,比较奇怪的是它没处理WM_ERASEBKGND,这样岂不让背景直接擦除了而有闪烁?

看了CButtonST的方法,即在DrawItem时首先取得父窗口的DC,然后计算和坐标变换后把按钮没创建前所在区域内容拷贝到一个内存DC,最后
用PDC显示出来.
我以同样的方法做了下,起初以为可以,测试中发现BUG:如果有TTPLAYER一类总在最前的软件,当这个DLG启动时,按钮区域恰好被TT遮住了.移出来
时发现背景变成了遮住的图. 我分析可能那方法不行,因为如果被遮盖了,所拷贝的区域内容肯定会包括遮盖的那部分,而不会是你DLG的背景.
处理的区别就是我在WM_ERASEBKGND直接返回TRUE;

CButtonST的DEMO是多页的,自己暂时没做测试,不知道会不会有类似的情况.
请各位给个方案..
...全文
2044 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
LICHUNLI1022 2011-04-14
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 vocanicy 的回复:]

解决方法很简单

因为CButtonST内部复制父窗口背景时,可能父窗口被遮挡并未绘制完成

只要在父窗口的绘制完背景后,主动调用一次CButtonST的SetBk函数就可以了
并且对于背景是动态的窗口也需要在背景变更后调用SetBk更新按钮内部保存的背景

以下是我的代码
注:我绘制背景的代码是在OnEraseBkgnd中的,你的代码可能和我不一样,根据自己的实际情况修改

……
[/Quote]
这句 m_btnSample.SetBk(pDC);的确能够解决遮挡是否能够正常显示的问题,但是却带来了新的问题。当光标停留在按钮上的时候,除了图标,其它都是黑色的。
古月无华 2008-11-30
  • 打赏
  • 举报
回复
你没有看清楚例子,在这个transpentDlg中,他用的不是CDialog,而是从CDialog继承的CBkDialogST,用这个类就能解决你的问题
j8daxue 2008-09-10
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 vocanicy 的回复:]
解决方法很简单

因为CButtonST内部复制父窗口背景时,可能父窗口被遮挡并未绘制完成

只要在父窗口的绘制完背景后,主动调用一次CButtonST的SetBk函数就可以了
并且对于背景是动态的窗口也需要在背景变更后调用SetBk更新按钮内部保存的背景

以下是我的代码
注:我绘制背景的代码是在OnEraseBkgnd中的,你的代码可能和我不一样,根据自己的实际情况修改


C/C++ code
BOOL CxxxView::OnEraseBkgnd(CDC* pDC) …
[/Quote]
这样做还会出问题,首先我使用的是GDI+,利用了а色,而且有使用双缓冲,而双缓冲使用的画布默认是黑色.
所以,在绘制按钮函数DrawButton(pDC)之前必须把背景设成父窗口的图.而此时就可能父窗口未绘制完.
希望9楼给个详细的说明, THX
hhoking 2008-09-08
  • 打赏
  • 举报
回复
恩,CButtonST的这个背景BUG我也发现了。简单的办法是不要保存背景图,而是每回都建新的。否则你就需要想办法去判断是否有遮挡了,假如有遮挡,在遮挡情况下做的背景图铁定不对。
vocanicy 2008-09-08
  • 打赏
  • 举报
回复
解决方法很简单

因为CButtonST内部复制父窗口背景时,可能父窗口被遮挡并未绘制完成

只要在父窗口的绘制完背景后,主动调用一次CButtonST的SetBk函数就可以了
并且对于背景是动态的窗口也需要在背景变更后调用SetBk更新按钮内部保存的背景

以下是我的代码
注:我绘制背景的代码是在OnEraseBkgnd中的,你的代码可能和我不一样,根据自己的实际情况修改


BOOL CxxxView::OnEraseBkgnd(CDC* pDC)
{
if((HDC)m_hBuffer != NULL)
{
::BitBlt(pDC->GetSafeHdc(), 0, 0, m_hSkin.Width(), m_hSkin.Height(),
m_hSkin, 0, 0, SRCCOPY);

m_btnSample.SetBk(pDC);
}

return TRUE;
}

j8daxue 2008-09-07
  • 打赏
  • 举报
回复
不会很难吧?没人知道吗?自己顶下
j8daxue 2008-09-06
  • 打赏
  • 举报
回复
很奇怪..如果WM_ERASEBKGND返回TRUE,则拷贝背景正确,默认返回的话则拷贝的内容却是该DLG左上角(0,0)到按钮长宽的位图.
代码如下
void XButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
CDC *pDC = CDC::FromHandle( lpDrawItemStruct->hDC );
m_ButRect = lpDrawItemStruct->rcItem; //获取按钮尺寸

if( m_strText.IsEmpty() )
GetWindowText( m_strText ); //获取按钮文本

int nSavedDC = pDC->SaveDC();
VERIFY( pDC );
//拷贝按钮所在的背景-将父窗口的DC部分内容BITBLT过来
CClientDC clDC(GetParent());
CBitmap m_bmpBk;
CRect rect;
CRect rect1;
GetClientRect(rect);
CRect rc = rect;
GetWindowRect(rect1);
GetParent()->ScreenToClient(rect1);
GetParent()->ScreenToClient(rc);
if (m_dcBk.m_hDC == NULL)
{
m_dcBk.CreateCompatibleDC(&clDC);
m_bmpBk.CreateCompatibleBitmap(&clDC, rect.Width(), rect.Height());
oldBmp = m_dcBk.SelectObject(&m_bmpBk);
m_dcBk.BitBlt(0, 0, rect.Width(), rect.Height(), &clDC, rect1.left ,rect1.top , SRCCOPY);
}

CDC dcMem;
dcMem.CreateCompatibleDC(pDC);
CBitmap bk;
bk.CreateCompatibleBitmap(pDC,m_ButRect.Width(),m_ButRect.Height());
dcMem.SelectObject(bk);

dcMem.BitBlt(0,0,m_ButRect.Width(),m_ButRect.Height(),&m_dcBk,0,0,SRCCOPY);
DrawButton( &dcMem); //绘制按钮
pDC->BitBlt(0,0,m_ButRect.Width(),m_ButRect.Height(),&dcMem,0,0,SRCCOPY);

pDC->RestoreDC( nSavedDC );
}


DrawButton()中和背景无关,就不贴了
zqh886 2008-09-06
  • 打赏
  • 举报
回复
转贴一篇:

最近,看了一些关于浮动按键的代码,其原理大致上跟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风格)

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

这个很容易实现,不在这里多说了。
qaz2008 2008-09-06
  • 打赏
  • 举报
回复
up
qqwx_1986 2008-09-06
  • 打赏
  • 举报
回复
学习
吹泡泡的小猫 2008-09-06
  • 打赏
  • 举报
回复
CButtonST很早就用过了,CButtonST在截取背景图象时用的是对话框的DC,应该不会把其它窗口的DC内容拷贝出来。

所有的绘制操作都在onpaint中完成了,你不应该再处理WM_ERASEBKGND,就让系统用默认背景擦除,因为默认背景就是对话框的背景,刚好是按钮需要的位图区域
cnzdgs 2008-09-06
  • 打赏
  • 举报
回复
这问题与WM_ERASEBKGND无关,你把窗口遮盖的情况再详细描述一下,确认一下在出问题的时候DrawItem是否执行了,如果执行了DrawItem,把DrawItem的代码贴出来看看。
LOVESKY_REN 2008-09-06
  • 打赏
  • 举报
回复
1,WINAPI_OCX.zip封装了部分Windows API的控件(92KB)2,HeaderCtrl.zip多行标题的CListCtrl(19KB)3,RoundBut.zip你需要圆形的按钮吗?这个类已经替你做好了,它可是有正常、平面、下推几种风格的,快使用它吧(25KB)4,TransBut.zip实现背景透明按钮类(306KB)5,AviButton.zip这个类库可以在按钮上显示AVI动画,很酷的(68KB)6,cirbutton.zip一个圆形的可下推按钮,还不错,可以试试看(50KB)7,anibutton.zip这是一个可以使用DIB显示动画的按钮类库,值得一试(186KB)8,bitbutton.zip这个类库允许你在按钮上使用位图和文字(9KB)9,CButtonST.zip只要你的程序中使用按钮,这个类库就使你可以轻松做出图文并茂的按钮来(133KB)10,hoverbut.zip这个类库是对鼠标敏感的按纽,你用它可以做出象Office助手提示选项那样的东东来(24KB)11,menubut.zip当用户单击一个按钮时弹出一个菜单(5KB)12,TrackBut.zip也是一个位图按钮。不过可比MFC提供的那个好多了!(222KB)13,Stranbut.zip你需要不规则形状的按钮吗?这个类已经替你做好了,使用它吧(67KB)14,tributton.zip你需要三角形的按钮吗?这个类已经替你做好了,使用它吧(31KB)15,butpicker.zip你想有一个选择颜色的下拉列表吗?胜至你想有一个选择图形的下拉列表吗?这个类是你所需的,下载一个回去试试,分析分析一定会有收获(85KB)16,flat_comb.zip你有没有想过在你的应用程序中加入"浮动"的组合框,就象Microsoft Office中的那样?用这个类就能轻松搞定(21KB)17,fontcombo.zip这是一个用于选择字体的组合框类库,而且直接可以预览(46KB)18,icon_comb.zip这是一个选择图标的组合框的类(2KB)19,mrucombo.zip这个聪明的组合框具有IE那样的自动纪录历史的功能,你最近使用过的文件它会个个记在帐上。需要设计"History"功能的朋友赶快下载一个吧!(21KB)20,mulcombo2.zip这是一个具有多列功能的组合框,如果你的选择项需要多列显示的话一定需要这个东东(44KB)21,autocomp.zip这个聪明的组合框可以根据你的部分输入和可选项替你自动完成,就像IE的地址栏一样。是不是很好?快下载一个吧(24KB)22,ColorSel.zip一个用于颜色选择的组合框的例子(41KB)23,DriveBox.zip一个用于选择驱动器的组合框,告诉你如何实现自画控件以及如何通过Windows Shell得到驱动器的图标(128KB)24,mccombobox.zip这是一个多列的组合框类库(22KB)25,mlistbox.zip这个例子讨论了列表框的单/复选问题,值得一看(22KB)26,checklist.zip这是一个多列且具有检查框的列表框。使用它,你可以制作诸如安装程序中的自定义安装明细表等等。(50KB)27,ListBoxEx.zip你知道怎么让列表框水平滚动吗?这个类会向你解释一切(85KB)28,FlatBox.zip浮动效果的列表框,很酷的!(3KB)29,ColorBox.zip这是一个可以以不同颜色显示列表项目的列表框类库(17KB)30,iconpick.zip一个图标选择的列表框(32KB)31,hexedit.zip这是一个从Cedit派生的十六进制的编辑框类库(30KB)32,histedit.zip注意过Visual C++的Output窗口

15,979

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 界面
社区管理员
  • 界面
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧