Winforms:平滑面板的圆角边缘

Winforms: Smooth the rounded edges for panel

我已经按照 this tutorial 创建了一个圆形面板。教程中的代码在 vb 中,但我能够将其转换为 C#,所以这是我的代码:

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

我没有使用边框,但是结果是一样的。这是一个ss:

我认为使用抗锯齿进行平滑处理可以解决问题,但我想我错了。问题是如何平滑边缘?

在您的代码中:

 private void ExtendedDraw(PaintEventArgs e)
{
    e.Graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
    e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
    e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    LinearGradientBrush brush = new LinearGradientBrush(ClientRectangle, Color.White, Color.White, 90); //here you need your target rectangle

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

    e.Graphics.FillPath(brush, path);
}

我按照 this link 解决了这个问题。我刚刚下载了示例项目并创建了一个新面板。将他在 Form 的 onpaint 上的内容复制到新面板的 onpaint,现在我有了平滑的边缘。

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

这是他的 GraphicsExtension class 如果 link 坏了。

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)); 
    }
}
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        [DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]
        private static extern IntPtr CreateRoundRectRgn
        (
            int nLeftRect,     // x-coordinate of upper-left corner
            int nTopRect,      // y-coordinate of upper-left corner
            int nRightRect,    // x-coordinate of lower-right corner
            int nBottomRect,   // y-coordinate of lower-right corner
            int nWidthEllipse, // height of ellipse
            int nHeightEllipse // width of ellipse
        );

        public Form1()
        {
            InitializeComponent();
            this.FormBorderStyle = FormBorderStyle.None;
            Region = System.Drawing.Region.FromHrgn(CreateRoundRectRgn(0, 0, Width, Height, 20, 20));
        }
    }
}

我在网上找到了解决方案并尝试成功!希望对你有帮助

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace windowsFormsApp
{
    public partial class Form2 : Form
    {
        [DllImport("Gdi32.dll", EntryPoint = "CreateRoundRectRgn")]

        private static extern IntPtr CreateRoundRectRgn
        (
            int nLeftRect,
            int nTopRect,
            int nRightRect,
            int nBottomRect,
            int nWidthEllipse,
            int nHeightEllipse
        );
        public Form2()
        {
            InitializeComponent();
            
        }

        private void Form2_Load(object sender, EventArgs e)
        {
            panel1.Region = Region.FromHrgn(CreateRoundRectRgn(0, 0, panel1.Width, 
            panel1.Height, 30, 30));
            

        }
    }
}

我也在尝试做同样的事情,最后我通过创建自定义控件做到了。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace your.namespace.here
{
public class CustomPanel : System.Windows.Forms.Panel
{
    private System.Windows.Forms.Panel panel;

    private Color borderColor = Color.MediumSlateBlue;
    private Color borderFocusColor = Color.HotPink;
    private int borderSize = 2;
    private bool underlinedStyle = false;
    private bool isFocused = false;

    private int borderRadius = 0;


    public Color BorderColor
    {
        get { return borderColor; }
        set
        {
            borderColor = value;
            this.Invalidate();
        }
    }

    public Color BorderFocusColor
    {
        get { return borderFocusColor; }
        set { borderFocusColor = value; }
    }

    public int BorderSize
    {
        get { return borderSize; }
        set
        {
            if (value >= 1)
            {
                borderSize = value;
                this.Invalidate();
            }
        }
    }

    public bool UnderlinedStyle
    {
        get { return underlinedStyle; }
        set
        {
            underlinedStyle = value;
            this.Invalidate();
        }
    }

    public override Color ForeColor
    {
        get { return base.ForeColor; }
        set
        {
            base.ForeColor = value;
            panel.ForeColor = value;
        }
    }


    public int BorderRadius
    {
        get { return borderRadius; }
        set
        {
            if (value >= 0)
            {
                borderRadius = value;
                this.Invalidate();//Redraw control
            }
        }
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        Graphics graph = e.Graphics;

        if (borderRadius > 1)//Rounded TextBox
        {
            //-Fields
            var rectBorderSmooth = this.ClientRectangle;
            var rectBorder = Rectangle.Inflate(rectBorderSmooth, -borderSize, -borderSize);
            int smoothSize = borderSize > 0 ? borderSize : 1;

            using (GraphicsPath pathBorderSmooth = GetFigurePath(rectBorderSmooth, borderRadius))
            using (GraphicsPath pathBorder = GetFigurePath(rectBorder, borderRadius - borderSize))
            using (Pen penBorderSmooth = new Pen(this.Parent.BackColor, smoothSize))
            using (Pen penBorder = new Pen(borderColor, borderSize))
            {
                //-Drawing
                this.Region = new Region(pathBorderSmooth);//Set the rounded region of UserControl
                if (borderRadius > 15) SetTextBoxRoundedRegion();//Set the rounded region of TextBox component
                graph.SmoothingMode = SmoothingMode.AntiAlias;
                penBorder.Alignment = System.Drawing.Drawing2D.PenAlignment.Center;
                if (isFocused) penBorder.Color = borderFocusColor;

                if (underlinedStyle) //Line Style
                {
                    //Draw border smoothing
                    graph.DrawPath(penBorderSmooth, pathBorderSmooth);
                    //Draw border
                    graph.SmoothingMode = SmoothingMode.None;
                    graph.DrawLine(penBorder, 0, this.Height - 1, this.Width, this.Height - 1);
                }
                else //Normal Style
                {
                    //Draw border smoothing
                    graph.DrawPath(penBorderSmooth, pathBorderSmooth);
                    //Draw border
                    graph.DrawPath(penBorder, pathBorder);
                }
            }
        }
    }
    private void SetTextBoxRoundedRegion()
    {
        GraphicsPath pathTxt;


        pathTxt = GetFigurePath(panel.ClientRectangle, borderSize * 2);
        panel.Region = new Region(pathTxt);

        pathTxt.Dispose();
    }
    private GraphicsPath GetFigurePath(Rectangle rect, int radius)
    {
        GraphicsPath path = new GraphicsPath();
        float curveSize = radius * 2F;

        path.StartFigure();
        path.AddArc(rect.X, rect.Y, curveSize, curveSize, 180, 90);
        path.AddArc(rect.Right - curveSize, rect.Y, curveSize, curveSize, 270, 90);
        path.AddArc(rect.Right - curveSize, rect.Bottom - curveSize, curveSize, curveSize, 0, 90);
        path.AddArc(rect.X, rect.Bottom - curveSize, curveSize, curveSize, 90, 90);
        path.CloseFigure();
        return path;
    }
}
}

输出如下:

注意:此代码仅适用于自定义面板。