请教如何解决双缓冲刷新和半透明控件显示背景的矛盾

cyfage 2012-08-07 09:08:37
在下在一个对话框上想绘制一个动画,因此需要使用双缓冲进行不断地刷新;然而在该对话框上又有需要半透明效果的控件(比如半透明的PNG按钮),使他们能透过本身显示出下面对话框背景的颜色。

个人在实际的使用中,发现以我掌握到的方法,对话框的双缓冲和半透明控件之间似乎很难共存。主要是如下几个方面造成的:
1、为了动态显示动画,必须使用双缓冲;
2、由于对话框不断的刷新——即使只刷新动画那一块,一样会造成对话框上控件的闪烁;
3、为了阻止对话框在重绘时不断刷新控件,于是我只能将对话框的“Clip Children”属性设置为true;
4、“Clip Children”属性设置后,对话框重绘时不去管它的子控件,闪是不会闪了,可半透明按钮“先绘制背景,再在该区域上绘制本按钮”的要求又达不到了。

对话框中的OnPaint()函数代码:

void CtestDlg::OnPaint()
{
CPaintDC dc(this); // device context for painting

CRect rect;
GetClientRect(&rect);
int Width = rect.right - rect.left;
int Height = rect.bottom - rect.top;

CDC *pDC = this->GetDC();

CDC MenmDC;
CBitmap MemBitmap;
MenmDC.CreateCompatibleDC(NULL);
MemBitmap.CreateCompatibleBitmap(pDC, Width, Height);
CBitmap *pOldBit = MenmDC.SelectObject(&MemBitmap);

/*********************************************************/
MenmDC.FillSolidRect(0, 0, Width, Height, RGB(255, 128, 0));
//根据变量计数,绘制当前进度条的长度
using namespace Gdiplus;
Graphics graphics(MenmDC.m_hDC);
graphics.DrawImage(m_ImgScroll, Rect(30, 120, m_ImgScroll->GetWidth() * ((float)m_Tick / (float)100), m_ImgScroll->GetHeight()), 0, 0, m_ImgScroll->GetWidth() * ((float)m_Tick / (float)100), m_ImgScroll->GetHeight(), UnitPixel);
/*********************************************************/

pDC->BitBlt(0, 0, Width, Height, &MenmDC, 0, 0, SRCCOPY);
MenmDC.SelectObject(pOldBit);
MemBitmap.DeleteObject();
MenmDC.DeleteDC();
ReleaseDC(pDC);
}


对话框中的OnEraseBkgnd(CDC* pDC)函数代码:

BOOL CtestDlg::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}


对话框中不断刷新重绘的代码:

void CtestDlg::OnTimer(UINT nIDEvent)
{
switch (nIDEvent)
{
case 1201:
{
++ m_Tick;
if (m_Tick >= 100)
{
m_Tick = 0;
}
//获取当前进度条需要绘制的长度,并刷新之
CRect rect;
rect.left = 30;
rect.top = 120;
rect.right = rect.left + m_ImgScroll->GetWidth() * ((float)m_Tick / (float)100);
rect.bottom = rect.top + m_ImgScroll->GetHeight();
InvalidateRect(&rect, FALSE);
}
break;
}
}


这是一张用来做按钮的图片,除了文字之外都是透明的,以便显示背景中的内容:


比如下图,在没有设置对话框的“Clip Children”属性为true时,半透明按钮是可以成功在对话框重绘之后重绘自己,从而正确半透明的显示背景的:

可是这样做的结果会造成按钮控件闪烁。

比如下图,在设置了对话框的“Clip Children”属性为true之后,控件不闪烁了,可是半透明的效果却没有了:


实在是有点没招了,求各位达人指教一下,谢谢……
这里是工程的代码
...全文
418 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
wutianshi 2014-08-14
  • 打赏
  • 举报
回复
想问一下 半透明效果怎么弄的?我的半透明效果是在对话框的初始化函数中 用user32..dll的方法做的。但是这么做会被一直刷屏的背景所覆盖(就是鼠标在对话框上点一下闪烁,移开鼠标,对话框(非模态)没有显示)。请问您的问题解决了吗?
  • 打赏
  • 举报
回复
引用 8 楼 tiger9991 的回复:
我后来想了下,这个方法BUG比较大,因为如果其他窗口压在主窗体上面,就刷新不了了。 要不用用看TransparentBlt,把贴图画到你的按钮上去。
TransparentBlt在双缓冲下不好弄吧。
cyfage 2012-08-10
  • 打赏
  • 举报
回复
看来只能继续使用双窗体的方式了
无论如何,多谢楼上各位。
cyfage 2012-08-08
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 的回复:]
确实矛盾的。
不过,楼主可以尝试下,让按钮控件完全不重绘,直接用背景图案压住按钮。(当做按钮不存在)
[/Quote]

老兄说的方法不是很理解,能麻烦解释下吗?
傻X 2012-08-08
  • 打赏
  • 举报
回复
我后来想了下,这个方法BUG比较大,因为如果其他窗口压在主窗体上面,就刷新不了了。
要不用用看TransparentBlt,把贴图画到你的按钮上去。
cyfage 2012-08-07
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]
按钮不用控件,直接用画的,跟背景一块画。
响应点击时判断鼠标是否点在上面就是了。
[/Quote]

[Quote=引用 3 楼 的回复:]
可以用2楼方法
还可以:
画按钮的时候,按照合适的坐标,把对话框的背景画到按钮背景
[/Quote]

多谢楼上两位,事实上这种方法我尝试过。
之前我了解到的有两种方法,一种就是你们两位说的画“模拟按钮”,不过
afx_msg void OnNcLButtonDown(UINT nHitTest, CPoint point);

好像只能用这个函数进行点击的判定,因为
afx_msg void OnNcLButtonUp(UINT nHitTest, CPoint point);

的点击判定似乎是有问题的,实际上没办法使用,这就导致点击的时候往往不太好使,和真正的按钮可以感觉出明显的不同。

实际上解决这个矛盾还有另外一个办法,就是构建双窗体,在底层窗口画图,上一层窗口刷成透明,然后放控件,但是这样做也有很多麻烦,主要原因是:

1、代码结构复杂,容易出错;

2、我能找到的所有双窗体结构都需要在上层窗口刷一个颜色,然后会导致大部分控件比如按钮会在边沿显示出一圈模糊的那种颜色,而且很难消除掉,非常麻烦,比如像这种代码:

m_brush.CreateSolidBrush(RGB(255, 0, 255));
DWORD dwExStyle = GetWindowLong(m_hWnd,GWL_EXSTYLE);
::SetWindowLong(m_hWnd, GWL_EXSTYLE, dwExStyle|0x80000);
::SetLayeredWindowAttributes(this->GetSafeHwnd(), 0xff00ff, 0, 1);


3、很多控件在透明的上层窗口使用都有问题,原因是各种控件自己的背景色一般都必须抹掉,所以只好使用这样的函数来解决:

afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);


HBRUSH DJL_BoxCtrl::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

if (IDC_STATIC_CTRL_MSG == pWnd->GetDlgCtrlID() ||
IDC_STATIC_CTRL_HELP == pWnd->GetDlgCtrlID() ||
IDC_STATIC_CTRL_CONTRIVE == pWnd->GetDlgCtrlID() ||
IDC_STATIC_CTRL_MEMBERCOUNT == pWnd->GetDlgCtrlID())
{
pDC->SetTextColor(RGB(255, 255, 255));
pDC->SetBkMode(TRANSPARENT);
return (HBRUSH)GetStockObject(NULL_BRUSH);
}

if(CTLCOLOR_DLG == nCtlColor)
{
return m_brush;
}

return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
}

即使经过这样的处理,仍然有很多控件会出问题。

4、双窗体还有一个莫名其妙的BUG:有时你点击窗口,经常会出现“点穿”的现象,导致窗口失去焦点,从屏幕最顶端消失。

5、使用双窗体会导致该窗口在屏幕录像时诡异的消失、无法显示在录像中的BUG。

当然这些出现的问题可能都和我没有掌握好有关,不过至少到目前为止我是没能解决掉这些BUG的……
所以我很想知道,到底有没有好的方法,让双缓冲的绘图和半透明控件在一个对话框上真正兼容……
eatsweetpotato 2012-08-07
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 的回复:]

按钮不用控件,直接用画的,跟背景一块画。
响应点击时判断鼠标是否点在上面就是了。
[/Quote]

如果界面上只有一两个还行,一旦数量多的话就该做成direct UI了
傻X 2012-08-07
  • 打赏
  • 举报
回复
确实矛盾的。

不过,楼主可以尝试下,让按钮控件完全不重绘,直接用背景图案压住按钮。(当做按钮不存在)
  • 打赏
  • 举报
回复
可以用2楼方法

还可以:
画按钮的时候,按照合适的坐标,把对话框的背景画到按钮背景
罗平 2012-08-07
  • 打赏
  • 举报
回复
按钮不用控件,直接用画的,跟背景一块画。
响应点击时判断鼠标是否点在上面就是了。
cyfage 2012-08-07
  • 打赏
  • 举报
回复
工程代码下载如果有问题可以从网盘上下载:
http://dl.dbank.com/c0nlez41d8

15,980

社区成员

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

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