Winforms控件中的投影效果?

21

有没有办法为控件添加阴影效果?

是否有带有此功能的控件可用?


我现在该怎么办?没有一个答案真正解决了这个问题... - Luiscencio
Simon的建议甚至适用于非矩形形状的窗体(通过区域),至少在Windows 7上。 - Uwe Keim
6个回答

34

你需要像这样覆盖CreateParams属性:

private const int CS_DROPSHADOW = 0x00020000;
protected override CreateParams CreateParams
{
    get
    {
        // add the drop shadow flag for automatically drawing
        // a drop shadow around the form
        CreateParams cp = base.CreateParams;
        cp.ClassStyle |= CS_DROPSHADOW;
        return cp;
    }
}

4
太棒了!还有其他类似的妙招的完整清单吗? - FrustratedWithFormsDesigner
9
我很喜欢你的昵称@FrustratedWithFormsDesigner,LOL。 - Luiscencio
@Simon Linder:这对表单很有效..你有没有想过如何将其应用到控件上? - Luiscencio
1
@FrustratedWithFormsDesigner:CS_DROPSHADOW是一个类样式。还有其他的,可以参考http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx和你SDK安装路径下的WinUser.h文件。 - Simon Linder
1
@SharpUrBrain:通常你不会在Loaded()事件中这样做,而是在构造函数或者InitializeComponents()中。你也可以尝试在构造函数中添加以下内容:SetStyle(ControlStyles.DoubleBuffer, true); UpdateStyles(); - Simon Linder
显示剩余8条评论

14
这个问题已经存在了六年,需要一个答案。我希望任何需要做这件事的人都能从我的解决方案中推断出任何控件集的答案。我有一个面板,并希望在每个子控件下面绘制一个投影阴影-在这种情况下是一个或多个面板(但解决方案应该适用于其他控件类型,只需进行一些小的代码更改)。
由于控件的投影阴影必须绘制在该控件容器的表面上,因此我们首先在容器的Paint()事件中添加一个函数。
Container.Paint += dropShadow;

dropShadow()的样式如下:

    private void dropShadow(object sender, PaintEventArgs e)
    {
        Panel panel = (Panel)sender;
        Color[] shadow = new Color[3];
        shadow[0] = Color.FromArgb(181, 181, 181);
        shadow[1] = Color.FromArgb(195, 195, 195);
        shadow[2] = Color.FromArgb(211, 211, 211);
        Pen pen = new Pen(shadow[0]);
        using (pen)
        {
            foreach (Panel p in panel.Controls.OfType<Panel>())
            {
                Point pt = p.Location;
                pt.Y += p.Height;
                for (var sp = 0; sp < 3; sp++)
                {
                    pen.Color = shadow[sp];
                    e.Graphics.DrawLine(pen, pt.X, pt.Y, pt.X + p.Width - 1, pt.Y);
                    pt.Y++;
                }
            }
        }
    }

显然,你可以从容器的集合中选择不同的控件类型,并且通过一些小的调整可以改变阴影的颜色和深度。


3
我测试了它,它是有效的,我做了一些更改...我改变了你的DrawLine并添加了第二条线... e.Graphics.DrawLine(pen, pt.X + sp, pt.Y, pt.X + p.Width - 1 + sp, pt.Y); e.Graphics.DrawLine(pen, p.Right + sp, p.Top + sp, p.Right + sp, p.Bottom + sp); 它看起来像这样:http://imgur.com/MfaR39s - Luiscencio
1
嗨,不错的调整 - 我只应用了底部阴影,因为我在模拟材料设计(特别是卡片),但我认为这种组合应该满足任何寻找解决方案的人。 - Mike

8

顶部的答案确实会生成一个阴影,但出于以下几个原因,我个人对它并不满意:

  • 它只适用于矩形(尽管WinForms控件都是矩形,但我们可能希望在其他情况下使用它)
  • 更重要的是:它不够平滑。 它看起来不像其他程序中的阴影那样自然。
  • 最后,它稍微有点麻烦。

因此,由于所有这些原因,我最终为我的项目编写了自己的代码,并想在这里分享它:

public partial class Form1 : Form
{
    List<Control> shadowControls = new List<Control>();
    Bitmap shadowBmp = null;
    public Form1()
    {
        InitializeComponent();
        shadowControls.Add(panel1);
        this.Refresh();
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        if (shadowBmp == null || shadowBmp.Size != this.Size)
        {
            shadowBmp?.Dispose();
            shadowBmp = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppArgb);
        }
        foreach (Control control in shadowControls)
        {
            using (GraphicsPath gp = new GraphicsPath())
            {
                gp.AddRectangle(new Rectangle(control.Location.X, control.Location.Y, control.Size.Width, control.Size.Height));
                DrawShadowSmooth(gp, 100, 60, shadowBmp);
            }
            e.Graphics.DrawImage(shadowBmp, new Point(0, 0));
        }
    }
    private static void DrawShadowSmooth(GraphicsPath gp, int intensity, int radius, Bitmap dest)
    {
        using (Graphics g = Graphics.FromImage(dest))
        {
            g.Clear(Color.Transparent);
            g.CompositingMode = CompositingMode.SourceCopy;
            double alpha = 0;
            double astep = 0;
            double astepstep = (double)intensity / radius / (radius / 2D);
            for (int thickness = radius; thickness > 0; thickness--)
            {
                using (Pen p = new Pen(Color.FromArgb((int)alpha, 0, 0, 0), thickness))
                {
                    p.LineJoin = LineJoin.Round;
                    g.DrawPath(p, gp);
                }
                alpha += astep;
                astep += astepstep;
            }
        }
    }
}

在这个实现中,所有添加到shadowControls的控件都将呈现出平滑的阴影效果。你应该能够为非矩形形状实现这一点,因为生成阴影的主要函数需要一个GraphicsPath。请注意,在将阴影绘制到表单之前,重要的是你先将其绘制到另一个位图上,因为主要函数需要使用SourceCopy合成模式才能正常工作,这意味着如果你不先将其绘制到另一个表面上,阴影后面的任何内容都将被完全替换,透明度方面就没有用处了。我正在回答一个十年前的问题,但希望这对某些人有所帮助!

这个方法能否应用于单个控件的绘制,比如一个面板,而不是应用于父控件的绘制?我不确定这是否是设计上的问题,但是只有应用于父控件的绘制才能让它正常工作...这是目前我找到的唯一好的解决方案,谢谢。 - Lee
当我在VB.NET中尝试时,所有东西都运行和执行了,但我从未看到任何阴影 - 有什么想法吗? - technonaut
对于厚度循环,前两个周期alpha不会是0吗?alpha从零开始,并且只在循环中的绘图后进行更改,然后添加astep,但在第一个循环中,astep也是零。 - undefined

2

如果您能够使用WPF,那么在WPF中有这个功能。但是,由于GDI+的能力有限,我认为在Windows Forms中没有替代方案。


如果他编写自定义控件并在重写的Paint方法中添加阴影效果,他可能可以做到这一点。 - FrustratedWithFormsDesigner
1
@FrustratedWithFormsDesigner - 是的,那篇文章就是在做这件事。但这仍然是一种可怕的、可怕的方式来完成一个相对简单的任务! - Andy Shellam
4
您提到的文章已不存在。 - AaA
1
编辑后,这个回答并没有提供太多信息。我认为最初的链接文章已经移到了这里:http://www.codeproject.com/Articles/19258/Transparent-drop-shadow-in-C-GDI。 - kjbartel

1
一种方法是将您的控件放在一个具有以下属性的表格布局中:
  • 3列(5px,100%,5px)
  • 3行(5px,100%,5px)
  • 您的控件
    • 左上角(0,0)
    • columnSpan = 2和rowSpan = 2
    • 0边距
    • 停靠设置为填充
  • 添加一个面板
    • 将其背景设置为您的阴影颜色
    • 行= 1,列= 3
    • 0边距
    • 停靠设置为填充
  • 再添加一个面板
    • 将其背景设置为您的阴影颜色
    • 行= 3,列= 1,columnSpan = 2
    • 0边距
    • 停靠设置为填充

这样可以通过调整外部列和行的大小来灵活调整大小。这是一个快速草图,以可视化设计师中表格布局的外观:

表格布局草图

表格布局草图2

应用程序中最终结果的实际截图:

屏幕截图


0
这里有一个有争议的观点,你可以不用编写代码来实现它。 将主面板的边框样式设置为Fixed Single。 在其下方创建3个面板,每个方向都比主面板多1像素。 这3个面板中的每一个都是浅灰色的。 虽然不完美但便宜且容易实现。

带伪阴影的面板


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接