How can I draw a rounded rectangle as the border for a rounded Form?
A base implementation of what is described in the comments.
The Form frmRoundCorners
provides some properties that allow to draw its rounded area with a custom BackColor, a custom BorderColor and a custom inner border color, acting as a shadow for the internal side of the Form's border.
The Form itself is implemented using a base class, baseForm
, derived from Form
, so the Form's properties can be set in the Form designer.
The transparency is activated setting the Form's original BackColor equal to its TrasparencyKey
, making its ClientArea
completely transparent, but drawable.
The Form's original border is set to FormBorderStyle.None
in the base class constructor.
I didn't set a specific BackColor/TransparencyKey
Color (it must be set in the Form's designer) because I think it's something one need to experiment with. I'd suggest a medium gray color. Avoid red components.
The Form can be moved, clicking on any point of its ClientArea
and dragging it.
The minimum/maximum curvature of the Form and its custom Border is set to 15
and 180
degrees. It cannot be changed to a different range using the PropertyGrid.
The rounded area of the Form and its border are drawn using the GraphicsPath.AddArc() method, then applying a Matrix transformation to the Graphics
object, both in the Scale
and the Transform
(position) components. The Size
component is untouched.
This is what it looks like:
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
[ToolboxItem(false)]
public partial class frmRoundCorners : baseForm
{
private GraphicsPath pathRegion = new GraphicsPath(FillMode.Winding);
private GraphicsPath pathBorder;
Point pMousePosition = Point.Empty;
public frmRoundCorners()
{
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw, true);
InitializeComponent();
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) {
pMousePosition = e.Location;
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) {
Point screenPos = PointToScreen(e.Location);
this.Location = new Point(screenPos.X - pMousePosition.X, screenPos.Y - pMousePosition.Y);
}
base.OnMouseMove(e);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
RoundedCornerRectangle(ClientRectangle);
RectangleF rect = pathRegion.GetBounds();
float scaleX = 1 - (BorderSize / rect.Width);
float scaleY = 1 - (BorderSize / rect.Height);
using (Pen pen = new Pen(BorderColor, BorderSize))
using (Pen penBorder = new Pen(InternalBorderColor, 2))
using (var brush = new SolidBrush(FillColor))
using (var mx = new Matrix(scaleX, 0, 0, scaleY, (pen.Width / 2), (pen.Width / 2)))
{
e.Graphics.Transform = mx;
e.Graphics.FillPath(brush, pathRegion);
e.Graphics.DrawPath(penBorder, pathBorder);
e.Graphics.DrawPath(pen, pathRegion);
}
}
private void RoundedCornerRectangle(Rectangle r)
{
pathRegion = new GraphicsPath(FillMode.Alternate);
float innerCurve = CurveAngle - m_PenSizeOffset;
pathRegion.StartFigure();
pathRegion.AddArc(r.X, r.Y, CurveAngle, CurveAngle, 180, 90);
pathRegion.AddArc(r.Right - CurveAngle, r.Y, CurveAngle, CurveAngle, 270, 90);
pathRegion.AddArc(r.Right - CurveAngle, r.Bottom - CurveAngle, CurveAngle, CurveAngle, 0, 90);
pathRegion.AddArc(r.X, r.Bottom - CurveAngle, CurveAngle, CurveAngle, 90, 90);
pathRegion.CloseFigure();
pathBorder = new GraphicsPath();
pathBorder.StartFigure();
pathBorder.AddArc(r.X + m_PenSizeOffset, r.Y + m_PenSizeOffset, innerCurve, innerCurve, 180, 90);
pathBorder.AddArc(r.Right - innerCurve - m_PenSizeOffset, r.Y + m_PenSizeOffset, innerCurve, innerCurve, 270, 90);
pathBorder.AddArc(r.Right - innerCurve - m_PenSizeOffset, r.Bottom - innerCurve- m_PenSizeOffset, innerCurve, innerCurve, 0, 90);
pathBorder.AddArc(r.X + m_PenSizeOffset, r.Bottom - innerCurve - m_PenSizeOffset, innerCurve, innerCurve, 90, 90);
pathBorder.CloseFigure();
}
}
baseForm
class:
public class baseForm : Form
{
private Color m_InternalBorderColor = Color.FromArgb(128, 128, 128);
private Color m_BorderColor = Color.Red;
private Color m_FillColor = Color.WhiteSmoke;
private float m_PenSize = 6f;
private float m_CurveAngle = 60.0f;
internal float m_PenSizeOffset = 3f;
public baseForm() => InitializeComponent();
private void InitializeComponent() => FormBorderStyle = FormBorderStyle.None;
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[DefaultValue(60.0f)]
public virtual float CurveAngle
{
get => m_CurveAngle;
set {
m_CurveAngle = Math.Max(Math.Min(value, 180), 15);
Invalidate();
}
}
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[DefaultValue(6.0f)]
public virtual float BorderSize
{
get => m_PenSize;
set {
m_PenSize = value;
m_PenSizeOffset = value / 2.0f;
Invalidate();
}
}
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public virtual Color BorderColor
{
get => m_BorderColor;
set {
m_BorderColor = value;
Invalidate();
}
}
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public virtual Color FillColor
{
get => m_FillColor;
set {
m_FillColor = value;
Invalidate();
}
}
[EditorBrowsable(EditorBrowsableState.Always), Browsable(true), Category("Appearance")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[Description("Get or Set the Color of the internal border")]
public virtual Color InternalBorderColor
{
get => m_InternalBorderColor;
set {
m_InternalBorderColor = value;
Invalidate();
}
}
[EditorBrowsable(EditorBrowsableState.Never), Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[DefaultValue(FormBorderStyle.None)]
public new FormBorderStyle FormBorderStyle
{
get => base.FormBorderStyle;
set => base.FormBorderStyle = FormBorderStyle.None;
}
}