Windows 窗口透明知识点

1。透明窗口要求:

窗口不能是Child类型

要使使窗体拥有透明效果,首先要有WS_EX_LAYERED扩展属性,方法可以在CreateWindowEx时指定,也可以SetWindowLong动态设置。如下代码可设置分层窗口:
LONG style = GetWindowLong(m_hWnd, GWL_EXSTYLE);
style |= WS_EX_LAYERED;
SetWindowLong(m_hWnd, GWL_EXSTYLE, style);
 

2。SetLayeredWindowAttributes

BOOL SetLayeredWindowAttributes(

HWND hwnd, // 指定分层窗口句柄

COLORREF crKey, // 指定需要透明的背景颜色值,可用RGB()宏

BYTE bAlpha, // 设置透明度,0表示完全透明,255表示不透明

DWORD dwFlags // 透明方式

);

其中,dwFlags参数可取以下值:

LWA_ALPHA时:crKey参数无效,bAlpha参数有效;

LWA_COLORKEY:窗体中的所有颜色为crKey的地方将变为透明,bAlpha参数无效。其常量值为1。

LWA_ALPHA | LWA_COLORKEY:crKey的地方将变为全透明,而其它地方根据bAlpha参数确定透明度。

::SetLayeredWindowAttributes((HWND)透明窗口handle, RGB(100,100,100), 128, LWA_ALPHA | LWA_COLORKEY);

// 获取鼠标下面所在的窗口句柄
m_hCurrWnd = ::WindowFromPoint(point);

if (g_pSetLayeredWindowAttributes && m_hCurrWnd != m_hWnd)
{
::SetWindowLong(m_hCurrWnd, GWL_EXSTYLE,
GetWindowLong(m_hCurrWnd,  GWL_EXSTYLE) ^ WS_EX_LAYERED);
g_pSetLayeredWindowAttributes(m_hCurrWnd, 0, (BYTE)m_slider.GetPos(), LWA_ALPHA);

::RedrawWindow(m_hCurrWnd, NULL, NULL,
RDW_ERASE | RDW_INVALIDATE |
RDW_FRAME | RDW_ALLCHILDREN);
}

LayeredWindow提供两种模式:

1.使用SetLayeredWindowAttributes去设置透明度, 完成窗口的统一透明,此时窗口仍然收到PAINT消息, 其他应用跟普通窗口一样.

2.使用UpdateLayeredWindow方法, 向系统提交包含bitmap的DC, 交由系统统一管理,此时再也收不到paint消息, 任何对窗口的改变,只能通过UpdateLayeredWindow来修改.

如果你不需要针对像素级别的不同透明,只需要使用SetLayeredWindowAttributes模式即可,用法与普通窗口用法一样,有一点不同,系统会缓存窗口的bitmap,所以当窗口上面的其他窗口被移开时,这是系统会去自己绘制,不会发送paint消息。使用这种模式的好处时,你基本不用改变你使用窗口的方法,你收到paint消息后,绘制的图像会被系统重定向到另一个函数里面,进行组合,从而得出透明效果。

如果你需要达到针对像素级别的不同透明,或者你想更加直接的去控制窗口的绘制,就必须使用UpdateLayeredWindow方法了,这个方法不重定向你的绘制结果,也不缓存窗口的bitmap,而是完全由你自己来绘制,这样在内存上来说,是更高效的。

1.一旦你调用了SetLayeredWindowAttributes,UpdateLayeredWindow的调用就会失败,你需要重新设置WS_EX_LAYERED,这是两种模式切换的关键。
2.WS_EX_LAYERED属性是不可以设置给子窗口
3.在完全透明的地方,鼠标的点击将会被穿过,另如果对窗口设置了WS_EX_TRANSPARENT属性,鼠标消息也会穿过

DM使用了以下方式来实现UpdateLayeredWindow窗口接收PAINT消息:

如果想把按钮也做成透明的,两个思路提供给楼主参考下:
一种方式是直接利用图片绘制上去,利用GDI的AlphaBlend或者GDI+的透明通道做透明绘制,只是这样这个按钮所有的行为都要自己去编写。
另一个是把这个按钮也做成窗口式窗体,利用SetLayeredWindowAttributes去设置透明。

---------为什么Winforms控件不支持半透明的背景颜色

https://blog.csdn.net/MYsce/article/details/77900743

整个Windows Forms的painting框架是基于GDI+。我们知道GDI+是支持透明色的,也就是支持带有alpha的颜色。那为什么绝大部分控件不知道半透明的背景颜色呢?这是Winforms受到了Win32 API的限制。

    在Winforms的所有控件中,只有Form和ToolStripDropDown支持Opacity属性。当Opacity属性的值为0时,为完全透明;当值为1时,则为完全不透明;当值在0和1之间时,则显示出半透明的效果。

    那么为什么其他控件不能实现半透明的效果呢?当我们深入到Winform的内部代码去分析Opacity的内部实现的时候,我们发现半透明效果需要控件支持WS_EX_LAYERED。从MSDN http://msdn.microsoft.com/en-us/library/ms632680(VS.85).aspx上我们发现WS_EX_LAYERED不能用于子窗口(Child Window)上。Form不能成为其他窗口的子窗口,而ToolStripDropDown只有在TopLevel为true的时候Opacity才有效,此时窗口的类型是pop-up而不是child类型。因此这两个类型的空间支持Opacity属性。而其他所有空间在创建的时候都用了WS_CHILD类型,都属于Child窗口,所以都不可能支持Opacity属性。

    如果希望其他空间支持半透明的背景颜色,我们除了BackColor的颜色含有alpha信息之外,还需要把ControlStyles.SupportsTransparentBackColor和ControlStyles.UserPaint设为true.例如我们希望得到背景颜色为透明的Label,我们可以自己从Label继承出一个类,并在该类的构造函数里调用Control.SetStyle函数把前面两个Style设为true.此时如果背景颜色的alpha值为0,则该Label的背景变成透明的了。值得一提的是,由于Control.UserPaint为true,我们需要自己负责Paint的全过程,Winforms不会自动把控件画出来。例如在缺省情况下,ListBox中的所有Item就不会画出来,我们需要在它的Paint事件处理器里完成Paint操作。

猜你喜欢

转载自blog.csdn.net/smartgps2008/article/details/90318801