65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (30投票s)

2007 年 6 月 19 日

3分钟阅读

viewsIcon

176769

downloadIcon

8328

如何为面板控件创建透明阴影效果。

Screenshot - dropshadow.png

引言

为了我正在进行的一个项目,我需要创建透明的模糊阴影,类似于各种 Windows 应用程序中的阴影。 也想要拥有它们吗? 那就继续阅读吧! 或者直接下载源代码...

背景

不幸的是,Visual Studio 2005/WinForms 完全不支持这些阴影,但 WPF 支持! 幸运的是,我只需要带有阴影的面板,并且矩形形状的阴影很容易实现。 我只是采用了我在网页上使用的方法,并将其应用于 WinForms。 诀窍如下:使用策略性放置和并排排列的阴影图片,我们可以模拟阴影效果。 当使用透明 PNG 时,它看起来就像真的一样。 我们需要的图像部分在下面用红色标出

Screenshot - shadow4.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.DrawingSystem.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;

首先,我们将在底部和右侧进行平铺图像

Screenshot - shadow1.png

我认为代码说明了一切。 只需在 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 应该会出现在工具箱中。 只需将其拖到窗体上并运行它。 您将有望获得以下结果

Screenshot - shadow2.png

顺便说一句:我将我的窗体的背景颜色设置为白色,所以它可能看起来与你的不同。

引入角

要在角中绘制小位图,只需添加以下内容

            // 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));

这太容易了。

填充和边框

我们已经有了 PanelColorBorderColor 属性。 现在是时候使用它们了。

            // 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);
            }

现在检查一下到目前为止的结果

Screenshot - shadow3.png

好了,就这些了。 我们快完成了。

总结

作为一种形式,以以下内容结束 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
© . All rights reserved.