半透明窗口上的非透明控件






4.19/5 (15投票s)
2006年7月6日
4分钟阅读

95322

4143
本文介绍了具有不透明子控件的半透明控件的使用及其工作原理。
1. 引言
通过设置窗口的透明度,可以改进 Windows 窗口的用户界面。在 Windows 2000 及更高版本中,这是通过为窗口设置 WS_EX_LAYERED
样式来实现的。但在这里,我们遇到了一个问题——该样式无法为子窗口设置。半透明窗口看起来很吸引人,但其上绘制的所有内容(包括控件)也将是半透明的。这严重影响了用户界面的可用性。
本文介绍了 KB_Soft Group 公司为内部需求开发的 SkinTooltip
控件,该控件能够实现半透明窗口上的不透明控件的效果。下面介绍的方法还可以实现控件在显示时的各种动画效果。
需要注意的是,该控件并非真正半透明,它只是模仿了透明度。因此,其使用会受到一些限制,下面将进行介绍。
2. 控件工作原理说明
我们解决这个问题的核心方法如下。控件本身(及其上的所有子控件)不是半透明的。半透明效果是通过模仿实现的。为此,执行两个步骤。
第一步,使用以下函数截取父窗口的屏幕截图:
public static void GetControlScreenshot( Control control,
ref Bitmap bitmap )
{
if (control == null || control.Handle == IntPtr.Zero)
return;
if (control.Width < 1 || control.Height < 1) return;
if (bitmap != null)
bitmap.Dispose();
// preparing the bitmap.
bitmap = new Bitmap(control.Width, control.Height);
Graphics destGraphics = Graphics.FromImage(bitmap);
// setting the flag indicating that we
// need to print both the client's and
// the non-client's window rectangles.
int printFlags = (int)( Win32API.PRF_NONCLIENT |
Win32API.PRF_CLIENT);
System.IntPtr param = new System.IntPtr(printFlags);
System.IntPtr hdc = destGraphics.GetHdc();
// sending the WM_PRINT message to the control window.
Win32API.SendMessage(control.Handle, Win32API.WM_PRINT, hdc, param);
destGraphics.ReleaseHdc(hdc);
destGraphics.Dispose();
}
该函数将控件窗口绘制到“位图”bitmap
中。这是通过 SendMessage
Win32 函数实现的。它向窗口发送 WM_PRINT
消息,其参数指定了输出的设备上下文。
父窗口的控件也会绘制在获取的图像上。然后将图像显示在控件的表面上,结果就是控件在父窗口上变得不可见。
第二步,我们将半透明窗口的背景设置为另一个 Bitmap
类对象。背景应呈现具有设置的半透明度(即具有 alpha 通道)的图像。所有子控件都绘制在该图像上(这对于实现动画是必需的)。获得的图像绘制在已显示的背景之上,所有控件都变得可见。结果是我们获得了具有不透明控件的半透明窗口效果。
动画是通过第二步获得的图像实现的。为了达到控件在父窗口背景上平滑出现的视觉效果,根据计时器消息,每个位图像素的 alpha 值乘以一个从 0 到 1.0 的乘数;结果是图像从完全透明变为在第二步指定为控件背景的图像中最初设置的值。为此,.NET Framework 库有一个基于 ColorMatrix
类的标准机制。使用该类,可以在图像的颜色显示在屏幕上之前指定要对其进行的颜色转换。
上述算法导致控件的使用受到一些限制。由于在显示之前只截取了父窗口所有控件状态的屏幕截图一次,因此其外观的变化可能会破坏半透明的错觉。在首次初始化半透明窗口后,其子控件的任何更改也可能导致控件出现故障。对于 KB_Soft Group 在开发这些控件时面临的任务,这些限制并非关键,但在其他情况下可能需要对组件进行改进。
3. 控件用法说明
该控件继承自 SkinControl
基类。该类的用法在“任意形状的控件”一文中有所描述。
下面描述了该类最重要的属性和方法:
AlphaAnimation
– 设置是否使用平滑透明度变化的动画。AnimationInterval
– 设置两个动画步骤之间的时间间隔。AnimationPosition
– 设置控件动画的起始位置。AnimationSteps
– 设置动画的步数。ExpandAnimation
- 设置通过改变控件大小的动画类型。ExpandType
– 设置通过改变控件大小的动画类型(移动、拉伸等)。FrontImage
– 设置控件的背景图像。Labels
– 半透明窗口上不透明标签的集合。
Animate()
– 启动控件动画的函数。
使用该控件最简单的方法是将其添加到 Visual Studio 环境的工具箱中,并设置其所有属性。但为了更精确地使用该控件,下面将介绍其手动创建方法:
创建一个新的 C# Windows 应用程序项目。添加一个新资源——result.png 图像。这将是控件的背景,也是指定其形状的模式。务必在资源的属性中将 Build Action 设置为 Embedded Resource。
向应用程序添加对 KBSoft.Components.dll 库的引用,并在包含窗体的文件的开头添加相应的 using
指令。
using KBSoft.Components;
现在向类添加一个新项:
private SkinTooltip skinTooltip = new SkinTooltip();
private Button btn = new Button();
将以下代码添加到构造函数中:
//getting the assembly for extracting the resources.
Assembly currentAssembly = Assembly.GetAssembly( this.GetType() );
//setting the pop-up animation.
skinTooltip.AlphaAnimation = true;
//setting the time interval between animation steps.
skinTooltip.AnimationInterval = 40;
//the position to display the control.
skinTooltip.AnimationPosition = new System.Drawing.Point(80, 24);
//the number of animation steps.
skinTooltip.AnimationSteps = 20;
//setting not to use animation by changing the control’s sizes.
skinTooltip.ExpandAnimation = false;
//setting the image of the control’s background.
skinTooltip.FrontImage = (Bitmap)Bitmap.FromStream(
currentAssembly.GetManifestResourceStream("TestControls.result.png") );
//setting the control’s shape.
skinTooltip.PatternBitmap = (Bitmap)Bitmap.FromStream(
currentAssembly.GetManifestResourceStream("TestControls.result.png") );
//setting the color of the image parts that
//are not included to the control’s //region
skinTooltip.TransparentColor = Color.FromArgb( 255, 255, 255 );
//creating the button and adding it to the list
//of the controls that are child for the skinTooltip control.
btn.Size = new Size(50,30);
btn.Location = new Point(150,30);
btn.Text = "Demo";
btn.FlatStyle = FlatStyle.System;
skinTooltip.Controls.Add( btn );
//the call needed for the control to function correctly.
skinTooltip.EndInit();
//necessarily set the control’s parent window.
skinTooltip.Parent = this;
结果如下图所示: