Drop shadow in Winforms Controls?

51.3k Views Asked by At

is there a way to add a drop shadow to controls?

are there any controls out there with this feature?

6

There are 6 best solutions below

2
On BEST ANSWER

This question has been around for 6 years and needs an answer. I hope that anyone who needs to do this can extrapolate an answer for any control set from my solution. I had a panel and wanted to draw a drop shadow underneath every child control - in this instance one or more panels (but the solution should hold good for other control types with some minor code changes).

As the drop shadow for a control has to be drawn on the surface of that control's container we start by adding a function to the container's Paint() event.

Container.Paint += dropShadow;

dropShadow() looks like this:

    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++;
                }
            }
        }
    }

Clearly you can pick a different control type from the container's collection and you can vary the colour and depth of the shadow with some minor tweaks.

4
On

There is in WPF if you can stretch to using that instead, I don't believe there is an alternative in Windows Forms due to the limited capabilities of GDI+.

13
On

You have to overwrite the CreateParamsproperty like this:

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;
    }
}
0
On

One way would be to put your control in a table layout with these properties:

  • 3 columns (5px, 100%, 5px)
  • 3 rows (5px, 100%, 5px)
  • Your control
    • top left corner (0,0)
    • columnSpan = 2 and rowSpan = 2
    • 0 margin
    • Dock set to fill
  • Add a panel
    • set with your shadow color as the background
    • row = 1, column = 3
    • 0 margin
    • Dock set to fill
  • Add another panel
    • set with your shadow color as the background
    • row = 3, column = 1, columnSpan = 2
    • 0 margin
    • Dock set to fill

This will give you flexibility over sizing by sizing the outer columns and rows. Here is a quick sketch to visualize what the table layout would look like in the designer:

Table Layout Sketch

Table Layout Sketch 2

Actual screenshot of the end result in an application:

Screenshot

0
On

Here's a controversial opinion, you do it without code. Set your main panel Border Style to Fixed Single. Create 3 panels below it, each 1 pixel larger in every direction. Each of the 3 panels is of a lighter shade of gray. Not perfect but cheap and easy.

panel with pseudo-shadow

3
On

The top answer does in fact generate a shadow, but I personally wasn't satisfied with it for a few reasons:

  • It only works for rectangles (granted, WinForms controls are all rectangles, but we might want to use this in other cases)
  • More importantly: It's not smooth. It doesn't look as natural as other shadows in other programs look.
  • Finally, it's slightly annoying to configure.

So, because of all these things, I ended up writing my own for my project and I thought I'd share it here:

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

In this implementation, all Controls added to the shadowControls will be painted with a smooth shadow. You should be able to implement this for non-rectangular shapes because the main function to generate the shadows takes a GraphicsPath. Please note that it's important you draw the shadow to another bitmap before drawing it to the form because the main function requires a compositing mode of SourceCopy to work, which means if you don't draw it to another surface first anything behind the shadow will be completely replaced and the transparency aspect is useless. I'm on a roll of answering 10-year-old questions, but hopefully, this helps someone!