Search code examples
c#winformsgraphicspanel

Winforms: Smooth the rounded edges for panel


I have followed this tutorial in order to create a rounded panel. The code in the tutorial is in vb but I was able to convert it to C# so here is my code:

    public class SPanel : Panel
{
    Pen pen;
    float penWidth = 2.0f;
    int _edge = 20;
    Color _borderColor = Color.White;
    public int Edge
    {
        get
        {
            return _edge;
        }
        set
        {
            _edge = value;
            Invalidate();
        }
    }

    public Color BorderColor
    {
        get
        {
            return _borderColor;
        }
        set
        {
            _borderColor = value;
            pen = new Pen(_borderColor, penWidth);
            Invalidate();
        }
    }

    public SPanel()
    {
        pen = new Pen(_borderColor, penWidth);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        ExtendedDraw(e);
        //DrawBorder(e.Graphics);
    }

    private void ExtendedDraw(PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        GraphicsPath path = new GraphicsPath();

        path.StartFigure();
        path.StartFigure();
        path.AddArc(GetLeftUpper(Edge), 180, 90);
        path.AddLine(Edge, 0, Width - Edge, 0);
        path.AddArc(GetRightUpper(Edge), 270, 90);
        path.AddLine(Width, Edge, Width, Height - Edge);
        path.AddArc(GetRightLower(Edge), 0, 90);
        path.AddLine(Width - Edge, Height, Edge, Height);
        path.AddArc(GetLeftLower(Edge), 90, 90);
        path.AddLine(0, Height - Edge, 0, Edge);
        path.CloseFigure();

        Region = new Region(path);
    }

    Rectangle GetLeftUpper(int e)
    {
        return new Rectangle(0, 0, e, e);
    }
    Rectangle GetRightUpper(int e)
    {
        return new Rectangle(Width - e, 0, e, e);
    }
    Rectangle GetRightLower(int e)
    {
        return new Rectangle(Width - e, Height - e, e, e);
    }
    Rectangle GetLeftLower(int e)
    {
        return new Rectangle(0, Height - e, e, e);
    }

    void DrawSingleBorder(Graphics graphics)
    {
        graphics.DrawArc(pen, new Rectangle(0, 0, Edge, Edge), 180, 90);
        graphics.DrawArc(pen, new Rectangle(Width - Edge -1, -1, Edge, Edge), 270, 90);
        graphics.DrawArc(pen, new Rectangle(Width - Edge - 1, Height - Edge - 1, Edge, Edge), 0, 90);
        graphics.DrawArc(pen, new Rectangle(0, Height - Edge - 1, Edge, Edge), 90, 90);

        graphics.DrawRectangle(pen, 0.0F, 0.0F, Width - 1, Height - 1);
    }

    void DrawBorder(Graphics graphics)
    {
        DrawSingleBorder(graphics);
    }
 }

I did not use the border however the result is the same. Here is a ss:

enter image description here

I thought smoothing with anti alias would do the trick but I guess i was wrong. The question is how can I smooth the edges?


Solution

  • I was able to solve this by following this link. I just downloaded the sample project and created a new panel. Copied what he had on Form's onpaint to new panel's onpaint and now I have smooth edges.

    public class SPanel : Panel
    {
        protected override void OnPaint(PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.FillRoundedRectangle(new SolidBrush(Color.White), 10, 10, this.Width - 40, this.Height - 60, 10);
            SolidBrush brush = new SolidBrush(
                Color.White
                );
            g.FillRoundedRectangle(brush, 12, 12, this.Width - 44, this.Height - 64, 10);
            g.DrawRoundedRectangle(new Pen(ControlPaint.Light(Color.White, 0.00f)), 12, 12, this.Width - 44, this.Height - 64, 10);
            g.FillRoundedRectangle(new SolidBrush(Color.White), 12, 12 + ((this.Height - 64) / 2), this.Width - 44, (this.Height - 64)/2, 10);
        }
    }
    

    Here is his GraphicsExtension class if link ever get broken.

    static class GraphicsExtension
    {
        private static GraphicsPath GenerateRoundedRectangle(
            this Graphics graphics, 
            RectangleF rectangle, 
            float radius)
        {
            float diameter;
            GraphicsPath path = new GraphicsPath();
            if (radius <= 0.0F)
            {
                path.AddRectangle(rectangle);
                path.CloseFigure();
                return path;
            }
            else
            {
                if (radius >= (Math.Min(rectangle.Width, rectangle.Height)) / 2.0)
                    return graphics.GenerateCapsule(rectangle);
                diameter = radius * 2.0F;
                SizeF sizeF = new SizeF(diameter, diameter);
                RectangleF arc = new RectangleF(rectangle.Location, sizeF);
                path.AddArc(arc, 180, 90);
                arc.X = rectangle.Right - diameter;
                path.AddArc(arc, 270, 90);
                arc.Y = rectangle.Bottom - diameter;
                path.AddArc(arc, 0, 90);
                arc.X = rectangle.Left;
                path.AddArc(arc, 90, 90);
                path.CloseFigure();
            }
            return path;
        }
        private static GraphicsPath GenerateCapsule(
            this Graphics graphics, 
            RectangleF baseRect)
        {
            float diameter;
            RectangleF arc;
            GraphicsPath path = new GraphicsPath();
            try
            {
                if (baseRect.Width > baseRect.Height)
                {
                    diameter = baseRect.Height;
                    SizeF sizeF = new SizeF(diameter, diameter);
                    arc = new RectangleF(baseRect.Location, sizeF);
                    path.AddArc(arc, 90, 180);
                    arc.X = baseRect.Right - diameter;
                    path.AddArc(arc, 270, 180);
                }
                else if (baseRect.Width < baseRect.Height)
                {
                    diameter = baseRect.Width;
                    SizeF sizeF = new SizeF(diameter, diameter);
                    arc = new RectangleF(baseRect.Location, sizeF);
                    path.AddArc(arc, 180, 180);
                    arc.Y = baseRect.Bottom - diameter;
                    path.AddArc(arc, 0, 180);
                }
                else path.AddEllipse(baseRect);
            }
            catch { path.AddEllipse(baseRect); }
            finally { path.CloseFigure(); }
            return path;
        }
    
        /// <summary>
        /// Draws a rounded rectangle specified by a pair of coordinates, a width, a height and the radius 
        /// for the arcs that make the rounded edges.
        /// </summary>
        /// <param name="brush">System.Drawing.Pen that determines the color, width and style of the rectangle.</param>
        /// <param name="x">The x-coordinate of the upper-left corner of the rectangle to draw.</param>
        /// <param name="y">The y-coordinate of the upper-left corner of the rectangle to draw.</param>
        /// <param name="width">Width of the rectangle to draw.</param>
        /// <param name="height">Height of the rectangle to draw.</param>
        /// <param name="radius">The radius of the arc used for the rounded edges.</param>
    
        public static void DrawRoundedRectangle(
            this Graphics graphics, 
            Pen pen, 
            float x, 
            float y, 
            float width, 
            float height, 
            float radius)
        {
            RectangleF rectangle = new RectangleF(x, y, width, height);
            GraphicsPath path = graphics.GenerateRoundedRectangle(rectangle, radius);
            SmoothingMode old = graphics.SmoothingMode;
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.DrawPath(pen, path);
            graphics.SmoothingMode = old;
        }
    
        /// <summary>
        /// Draws a rounded rectangle specified by a pair of coordinates, a width, a height and the radius 
        /// for the arcs that make the rounded edges.
        /// </summary>
        /// <param name="brush">System.Drawing.Pen that determines the color, width and style of the rectangle.</param>
        /// <param name="x">The x-coordinate of the upper-left corner of the rectangle to draw.</param>
        /// <param name="y">The y-coordinate of the upper-left corner of the rectangle to draw.</param>
        /// <param name="width">Width of the rectangle to draw.</param>
        /// <param name="height">Height of the rectangle to draw.</param>
        /// <param name="radius">The radius of the arc used for the rounded edges.</param>
    
        public static void DrawRoundedRectangle(
            this Graphics graphics, 
            Pen pen, 
            int x, 
            int y, 
            int width, 
            int height, 
            int radius)
        {
            graphics.DrawRoundedRectangle(
                pen, 
                Convert.ToSingle(x), 
                Convert.ToSingle(y), 
                Convert.ToSingle(width), 
                Convert.ToSingle(height), 
                Convert.ToSingle(radius));
        }
    
        /// <summary>
        /// Fills the interior of a rounded rectangle specified by a pair of coordinates, a width, a height
        /// and the radius for the arcs that make the rounded edges.
        /// </summary>
        /// <param name="brush">System.Drawing.Brush that determines the characteristics of the fill.</param>
        /// <param name="x">The x-coordinate of the upper-left corner of the rectangle to fill.</param>
        /// <param name="y">The y-coordinate of the upper-left corner of the rectangle to fill.</param>
        /// <param name="width">Width of the rectangle to fill.</param>
        /// <param name="height">Height of the rectangle to fill.</param>
        /// <param name="radius">The radius of the arc used for the rounded edges.</param>
    
        public static void FillRoundedRectangle(
            this Graphics graphics, 
            Brush brush, 
            float x, 
            float y, 
            float width, 
            float height, 
            float radius)
        {
            RectangleF rectangle = new RectangleF(x, y, width, height);
            GraphicsPath path = graphics.GenerateRoundedRectangle(rectangle, radius);
            SmoothingMode old = graphics.SmoothingMode;
            graphics.SmoothingMode = SmoothingMode.AntiAlias;
            graphics.FillPath(brush, path);
            graphics.SmoothingMode = old;
        }
    
        /// <summary>
        /// Fills the interior of a rounded rectangle specified by a pair of coordinates, a width, a height
        /// and the radius for the arcs that make the rounded edges.
        /// </summary>
        /// <param name="brush">System.Drawing.Brush that determines the characteristics of the fill.</param>
        /// <param name="x">The x-coordinate of the upper-left corner of the rectangle to fill.</param>
        /// <param name="y">The y-coordinate of the upper-left corner of the rectangle to fill.</param>
        /// <param name="width">Width of the rectangle to fill.</param>
        /// <param name="height">Height of the rectangle to fill.</param>
        /// <param name="radius">The radius of the arc used for the rounded edges.</param>
    
        public static void FillRoundedRectangle(
            this Graphics graphics, 
            Brush brush, 
            int x, 
            int y, 
            int width, 
            int height, 
            int radius)
        {
            graphics.FillRoundedRectangle(
                brush, 
                Convert.ToSingle(x), 
                Convert.ToSingle(y), 
                Convert.ToSingle(width), 
                Convert.ToSingle(height), 
                Convert.ToSingle(radius)); 
        }
    }