C# 中的透明阴影(GDI+)






4.70/5 (30投票s)
2007 年 6 月 19 日
3分钟阅读

176769

8328
如何为面板控件创建透明阴影效果。
引言
为了我正在进行的一个项目,我需要创建透明的模糊阴影,类似于各种 Windows 应用程序中的阴影。 也想要拥有它们吗? 那就继续阅读吧! 或者直接下载源代码...
背景
不幸的是,Visual Studio 2005/WinForms 完全不支持这些阴影,但 WPF 支持! 幸运的是,我只需要带有阴影的面板,并且矩形形状的阴影很容易实现。 我只是采用了我在网页上使用的方法,并将其应用于 WinForms。 诀窍如下:使用策略性放置和并排排列的阴影图片,我们可以模拟阴影效果。 当使用透明 PNG 时,它看起来就像真的一样。 我们需要的图像部分在下面用红色标出
通常,我会从创建这些图像开始。 对你来说幸运的是,我已经完成了,并且在源代码中提供了这些图像。 现在是开始编码的时候了!
项目
转到“文件”>“新建”>“项目”,然后选择“类库”。 将其命名为 ShadowPanel,然后单击“确定”。 现在将 *class1.cs* 重命名为 *ShadowPanel.cs*。 这就是我们将要使用的文件。 创建一个测试项目总是很方便的。 因此,在解决方案资源管理器中右键单击解决方案,然后选择“添加”>“新建项目”。 这次选择“Windows 应用程序”,并将其命名为 ShadowPanelTest。
属性
首先,我们需要一些属性。 提供至少一个边框和一个背景颜色会很好。 因此,现在是打开 *ShadowPanel.cs* 并添加以下内容的时候了
using System;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace ShadowPanel
{
public class ShadowPanel : Panel
{
private Color _panelColor;
public Color PanelColor
{
get { return _panelColor; }
set { _panelColor = value; }
}
private Color _borderColor;
public Color BorderColor
{
get { return _borderColor; }
set { _borderColor = value; }
}
我们不能使用原始的面板 BackgroundColor
属性,因为它是用于整个面板的,包括阴影区域! 因此,我创建了一个 PanelColor
属性,用于定义阴影内部区域的颜色。 关于对 System.Drawing
和 System.Drawing.Drawing2D
的引用:您可能需要通过右键单击解决方案资源管理器中的“引用”并在列表中选择它们来手动添加它们。
图像
现在是我们可以用来实际绘制阴影的图像。 如果你还没有这样做,你可以从源代码中下载它们。 在项目中创建一个名为 Images 的文件夹,并添加 *tshadowXXXX.png* 文件。 在解决方案资源管理器中选择它们,并将“生成操作”更改为“嵌入的资源”。 这样,文件将被编译到 DLL 中,您就不需要将其与您的应用程序一起分发。
我将这些图像作为静态添加到代码中;感谢 Remco Schrijvers 提出这个建议。 我还添加了两个私有成员来描述图像的大小——它们是 5×5 像素图像——以及阴影从左侧和顶部的偏移量(边距)。 请将以下代码添加到 ShadowPanel
类中
private int shadowSize = 5;
private int shadowMargin = 2;
// static for good performance
static Image shadowDownRight =
new Bitmap(typeof(ShadowPanel), "Images.tshadowdownright.png");
static Image shadowDownLeft =
new Bitmap(typeof(ShadowPanel), "Images.tshadowdownleft.png");
static Image shadowDown =
new Bitmap(typeof(ShadowPanel), "Images.tshadowdown.png");
static Image shadowRight =
new Bitmap(typeof(ShadowPanel), "Images.tshadowright.png");
static Image shadowTopRight =
new Bitmap(typeof(ShadowPanel), "Images.tshadowtopright.png");
OnPaint()
现在一切都准备好了,我们可以做一些很酷的事情了:创建 GDI+ 绘图代码! 让我们重写 Panel
基类的 OnPaint
方法。
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Get the graphics object. We need something to draw with
Graphics g = e.Graphics;
首先,我们将在底部和右侧进行平铺图像
我认为代码说明了一切。 只需在 Graphics.g = e.Graphics;
之后添加以下内容
// Create tiled brushes for the shadow on the
// right and at the bottom.
TextureBrush shadowRightBrush =
new TextureBrush(shadowRight, WrapMode.Tile);
TextureBrush shadowDownBrush =
new TextureBrush(shadowDown, WrapMode.Tile);
// Translate (move) the brushes so the top or
// left of the image matches the top or left of the
// area where it's drawed. If you don't understand why
// this is necessary, comment it out.
// Hint: The tiling would start at 0,0 of the control, so
// the shadows will be offset a little.
shadowDownBrush.TranslateTransform(0, Height - shadowSize);
shadowRightBrush.TranslateTransform(Width - shadowSize, 0);
// Define the rectangles that will be filled with the brush.
// (where the shadow is drawn)
Rectangle shadowDownRectangle = new Rectangle(
shadowSize + shadowMargin, // X
Height - shadowSize, // Y
Width - (shadowSize * 2 + shadowMargin),// width (stretches)
shadowSize // height
);
Rectangle shadowRightRectangle = new Rectangle(
Width - shadowSize, // X
shadowSize + shadowMargin, // Y
shadowSize, // width
Height - (shadowSize * 2 + shadowMargin) // height (stretches)
);
// And draw the shadow on the right and at the bottom.
g.FillRectangle(shadowDownBrush, shadowDownRectangle);
g.FillRectangle(shadowRightBrush, shadowRightRectangle);
您可以通过打开 *Form1.cs* 设计器将 ShadowPanel 添加到测试项目中。 如果您构建该项目一次,ShadowPanel 应该会出现在工具箱中。 只需将其拖到窗体上并运行它。 您将有望获得以下结果
顺便说一句:我将我的窗体的背景颜色设置为白色,所以它可能看起来与你的不同。
引入角
要在角中绘制小位图,只需添加以下内容
// Now for the corners, draw the 3 5×5 pixel images.
g.DrawImage(shadowTopRight, new Rectangle(Width - shadowSize,
shadowMargin, shadowSize, shadowSize));
g.DrawImage(shadowDownRight, new Rectangle(Width - shadowSize,
Height - shadowSize, shadowSize, shadowSize));
g.DrawImage(shadowDownLeft, new Rectangle(shadowMargin,
Height - shadowSize, shadowSize, shadowSize));
这太容易了。
填充和边框
我们已经有了 PanelColor
和 BorderColor
属性。 现在是时候使用它们了。
// Fill the area inside with the color in the PanelColor property.
// 1 pixel is added to everything to make the rectangle smaller.
// This is because the 1 pixel border is actually
// drawn outside the rectangle.
Rectangle fullRectangle = new Rectangle(
1, // X
1, // Y
Width - (shadowSize + 2), // Width
Height - (shadowSize + 2) // Height
);
if (PanelColor != null)
{
SolidBrush bgBrush = new SolidBrush(_panelColor);
g.FillRectangle(bgBrush, fullRectangle);
}
// Draw a nice 1 pixel border it a BorderColor is specified
if (_borderColor != null)
{
Pen borderPen = new Pen(BorderColor);
g.DrawRectangle(borderPen, fullRectangle);
}
现在检查一下到目前为止的结果
好了,就这些了。 我们快完成了。
总结
作为一种形式,以以下内容结束 OnPaint
方法
// Memory efficiency
shadowDownBrush.Dispose();
shadowRightBrush.Dispose();
shadowDownBrush = null;
shadowRightBrush = null;
}
为了确保控件在调整大小时正确重绘,我们需要重写 OnResize
方法。 谢谢,BobishKindaGuy!
// Correct resizing
protected override void OnResize(EventArgs e)
{
base.Invalidate();
base.OnResize(e);
}
}
}
完成!
关注点
当然,我不会阻止你添加更多功能。 请这样做! 另外,如果您喜欢本教程,请告诉我!
历史
- 2007 年 6 月 17 日 - 第一个版本
- 2007 年 6 月 30 日 - 添加了
OnResize