uwekeim icon

Draw drop shadow like CSS3 http://stackoverflow.com/a/13653

uwekeim | PRO | 05/06/13 06:44:29 AM UTC | 0 ⭐ | 957 👁️ | Never ⏰ | []
C# |

6.8 KB

|

None

|

0 👍

/

0 👎

namespace WindowsFormsApplication4
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Linq;
    using System.Windows.Forms;
 
    public class DropShadowPanel : Panel
    {
        protected override void OnControlAdded(ControlEventArgs e)
        {
            e.Control.Paint += Control_Paint;
            base.OnControlAdded(e);
        }
 
        void Control_Paint(object sender, PaintEventArgs e)
        {
            CheckDrawInnerShadow(sender as Control, e.Graphics);
        }
 
        private void CheckDrawInnerShadow(Control sender, Graphics g)
        {
            var dropShadowStruct = GetDropShadowStruct(sender);
 
            if (dropShadowStruct == null || !dropShadowStruct.Inset)
            {
                return;
            }
 
            DrawInsetShadow(sender, g);
 
        }
 
        protected override void OnControlRemoved(ControlEventArgs e)
        {
            e.Control.Paint -= Control_Paint;
        }
 
        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            DrawShadow(Controls.OfType<Control>().Where(c => c.Tag != null && c.Tag.ToString().StartsWith("DropShadow")), e.Graphics);
        }
 
        void DrawInsetShadow(Control control, Graphics g)
        {
            var dropShadowStruct = GetDropShadowStruct(control);
 
            var rInner = new Rectangle(Point.Empty, control.Size);
 
            var img = new Bitmap(rInner.Width, rInner.Height, g);
            var g2 = Graphics.FromImage(img);
 
            g2.CompositingMode = CompositingMode.SourceCopy;
            g2.FillRectangle(new SolidBrush(dropShadowStruct.Color), 0, 0, control.Width, control.Height);
 
            rInner.Offset(dropShadowStruct.HShadow, dropShadowStruct.VShadow);
            rInner.Inflate(dropShadowStruct.Blur, dropShadowStruct.Blur);
            rInner.Inflate(-dropShadowStruct.Spread, -dropShadowStruct.Spread);
 
            double blurSize = dropShadowStruct.Blur;
            double blurStartSize = blurSize;
 
            do
            {
                var transparency = blurSize / blurStartSize;
                var color = Color.FromArgb(((int)(255 * (transparency * transparency))), dropShadowStruct.Color);
                rInner.Inflate(-1, -1);
                DrawRoundedRectangle(g2, rInner, (int)blurSize, Pens.Transparent, color);
                blurSize--;
            } while (blurSize > 0);
 
            g.DrawImage(img, 0, 0);
            g.Flush();
 
            g2.Dispose();
            img.Dispose();
        }
 
        void DrawShadow(IEnumerable<Control> controls, Graphics g)
        {
            foreach (var control in controls)
            {
                var dropShadowStruct = GetDropShadowStruct(control);
 
                if (dropShadowStruct.Inset)
                {
                    continue; // must be handled by the control itself
                }
 
                DrawOutsetShadow(g, dropShadowStruct, control);
            }
        }
 
        // drawing the loop on an image because of speed
        private void DrawOutsetShadow(Graphics g, dynamic dropShadowStruct, Control control)
        {
            var rOuter = control.Bounds;
            var rInner = control.Bounds;
            rInner.Offset(dropShadowStruct.HShadow, dropShadowStruct.VShadow);
            rInner.Inflate(-dropShadowStruct.Blur, -dropShadowStruct.Blur);
            rOuter.Inflate(dropShadowStruct.Spread, dropShadowStruct.Spread);
            rOuter.Offset(dropShadowStruct.HShadow, dropShadowStruct.VShadow);
            var originalOuter = rOuter;
 
            var img = new Bitmap(originalOuter.Width, originalOuter.Height, g);
            var g2 = Graphics.FromImage(img);
 
            var currentBlur = 0;
 
            do
            {
                var transparency = (rOuter.Height - rInner.Height) / (double)(dropShadowStruct.Blur * 2 + dropShadowStruct.Spread * 2);
                var color = Color.FromArgb(((int)(255 * (transparency * transparency))), dropShadowStruct.Color);
                var rOutput = rInner;
                rOutput.Offset(-originalOuter.Left, -originalOuter.Top);
                DrawRoundedRectangle(g2, rOutput, currentBlur, Pens.Transparent, color);
                rInner.Inflate(1, 1);
                currentBlur = (int)((double)dropShadowStruct.Blur * (1 - (transparency * transparency)));
            } while (rOuter.Contains(rInner));
 
            g2.Flush();
            g2.Dispose();
 
            g.DrawImage(img, originalOuter);
 
            img.Dispose();
        }
 
        private static dynamic GetDropShadowStruct(Control control)
        {
            if (control.Tag == null || !(control.Tag is string) || !control.Tag.ToString().StartsWith("DropShadow"))
                return null;
 
            string[] dropShadowParams = control.Tag.ToString().Split(':')[1].Split(',');
            var dropShadowStruct = new
            {
                HShadow = Convert.ToInt32(dropShadowParams[0]),
                VShadow = Convert.ToInt32(dropShadowParams[1]),
                Blur = Convert.ToInt32(dropShadowParams[2]),
                Spread = Convert.ToInt32(dropShadowParams[3]),
                Color = ColorTranslator.FromHtml(dropShadowParams[4]),
                Inset = dropShadowParams[5].ToLowerInvariant() == "inset"
            };
            return dropShadowStruct;
        }
 
        private void DrawRoundedRectangle(Graphics gfx, Rectangle bounds, int cornerRadius, Pen drawPen, Color fillColor)
        {
            int strokeOffset = Convert.ToInt32(Math.Ceiling(drawPen.Width));
            bounds = Rectangle.Inflate(bounds, -strokeOffset, -strokeOffset);
 
            var gfxPath = new GraphicsPath();
            if (cornerRadius > 0)
            {
                gfxPath.AddArc(bounds.X, bounds.Y, cornerRadius, cornerRadius, 180, 90);
                gfxPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y, cornerRadius, cornerRadius, 270, 90);
                gfxPath.AddArc(bounds.X + bounds.Width - cornerRadius, bounds.Y + bounds.Height - cornerRadius, cornerRadius,
                               cornerRadius, 0, 90);
                gfxPath.AddArc(bounds.X, bounds.Y + bounds.Height - cornerRadius, cornerRadius, cornerRadius, 90, 90);
            }
            else
            {
                gfxPath.AddRectangle(bounds);
            }
            gfxPath.CloseAllFigures();
 
            gfx.FillPath(new SolidBrush(fillColor), gfxPath);
            if (drawPen != Pens.Transparent)
            {
                var pen = new Pen(drawPen.Color);
                pen.EndCap = pen.StartCap = LineCap.Round;
                gfx.DrawPath(pen, gfxPath);
            }
        }
    }
}

Comments