Hola, en esta ocasión les enseñaré cómo crear un formulario con esquinas redondeadas suaves en C#, Visual Basic y Windows Forms. Bueno, esto es casi imposible en Windows Form, como muchos saben, aplicar solo una región redondeada al formulario hará que las esquinas se vean pixeladas o entrecortadas, en un control se puede solucionar fácilmente dibujando un borde exterior con el mismo color del formulario o control contenedor, como les mostré en los tutoriales anteriores de controles personalizados. Sin embargo, eso me dio la idea de hacer lo mismo con un formulario, es decir, obtener el color de fondo posterior al formulario y dibujar un borde exterior, pero el fondo posterior al formulario no tendrá un solo color, como se muestra en mi escritorio, es una imagen de muchos colores, por lo que será necesario obtener colores de al menos las 4 esquinas del formulario para que se mezcle correctamente con el fondo y así aparentar unas esquinas redondeadas lisas. Sin embargo, para un mejor resultado, recomiendo obtener más colores de los puntos clave de los límites del formulario y con ello dibujar un borde degradado.
Campos & Constructor
public partial class Form1 : Form { //Fields private int borderRadius = 20; private int borderSize = 2; private Color borderColor = Color.FromArgb(128, 128, 255); //Constructor public Form1() { InitializeComponent(); this.FormBorderStyle = FormBorderStyle.None; this.Padding = new Padding(borderSize); this.panelTitleBar.BackColor = borderColor; this.BackColor = borderColor; }
Public Class Form1 'Fields Private borderRadius As Integer = 30 Private borderSize As Integer = 3 Private borderColor As Color = Color.HotPink 'Constructor Public Sub New() ' This call is required by the designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. Me.FormBorderStyle = FormBorderStyle.None Me.Padding = New Padding(borderSize) Me.PanelTitleBar.BackColor = borderColor Me.BackColor = borderColor End Sub
Método Mover/Arrastrar Formulario
//Drag Form [DllImport("user32.DLL", EntryPoint = "ReleaseCapture")] private extern static void ReleaseCapture(); [DllImport("user32.DLL", EntryPoint = "SendMessage")] private extern static void SendMessage(System.IntPtr hWnd, int wMsg, int wParam, int lParam); private void panelTitleBar_MouseDown(object sender, MouseEventArgs e) { ReleaseCapture(); SendMessage(this.Handle, 0x112, 0xf012, 0); }
'Drag Form <DllImport("user32.DLL", EntryPoint:="ReleaseCapture")> Private Shared Sub ReleaseCapture() End Sub <DllImport("user32.DLL", EntryPoint:="SendMessage")> Private Shared Sub SendMessage(ByVal hWnd As System.IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) End Sub Private Sub panelTitleBar_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseDown, PanelTitleBar.MouseDown ReleaseCapture() SendMessage(Me.Handle, &H112, &HF012, 0) End Sub
Minimizar Formulario sin Bordes desde la Barra de tareas
protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; cp.Style |= 0x20000; // <--- Minimize borderless form from taskbar return cp; } }
'Minimize borderless form from taskbar Protected Overrides ReadOnly Property CreateParams As CreateParams Get Dim cp As CreateParams = MyBase.CreateParams cp.Style = cp.Style Or &H20000 '<--- Minimize borderless form from taskbar Return cp End Get End Property
Métodos Privados
Crear Ruta Redondeada
private GraphicsPath GetRoundedPath(Rectangle rect, float 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; }
Private Function GetRoundedPath(rect As Rectangle, radius As Single) As GraphicsPath Dim path As GraphicsPath = New GraphicsPath() Dim curveSize As Single = radius * 2.0F 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 End Function
Establecer Región y Borde Redondeado – Formulario
private void FormRegionAndBorder(Form form, float radius, Graphics graph, Color borderColor, float borderSize) { if (this.WindowState != FormWindowState.Minimized) { using (GraphicsPath roundPath = GetRoundedPath(form.ClientRectangle, radius)) using (Pen penBorder = new Pen(borderColor, borderSize)) using (Matrix transform = new Matrix()) { graph.SmoothingMode = SmoothingMode.AntiAlias; form.Region = new Region(roundPath); if (borderSize >= 1) { Rectangle rect = form.ClientRectangle; float scaleX = 1.0F - ((borderSize + 1) / rect.Width); float scaleY = 1.0F - ((borderSize + 1) / rect.Height); transform.Scale(scaleX, scaleY); transform.Translate(borderSize / 1.6F, borderSize / 1.6F); graph.Transform = transform; graph.DrawPath(penBorder, roundPath); } } } }
Private Sub FormRegionAndBorder(form As Form, radius As Single, graph As Graphics, borderColor As Color, borderSize As Single) If Me.WindowState <> FormWindowState.Minimized Then Using roundPath As GraphicsPath = GetRoundedPath(form.ClientRectangle, radius) Using penBorder As Pen = New Pen(borderColor, borderSize) Using transform As Matrix = New Matrix() graph.SmoothingMode = SmoothingMode.AntiAlias form.Region = New Region(roundPath) If borderSize >= 1 Then Dim rect As Rectangle = form.ClientRectangle Dim scaleX As Single = 1.0F - ((borderSize + 1) / rect.Width) Dim scaleY As Single = 1.0F - ((borderSize + 1) / rect.Height) transform.Scale(scaleX, scaleY) transform.Translate(borderSize / 1.6F, borderSize / 1.6F) graph.Transform = transform graph.DrawPath(penBorder, roundPath) End If End Using End Using End Using End If End Sub
Establecer Región y Borde Redondeado – Panel Contenedor
private void ControlRegionAndBorder(Control control, float radius, Graphics graph, Color borderColor) { using (GraphicsPath roundPath = GetRoundedPath(control.ClientRectangle, radius)) using (Pen penBorder = new Pen(borderColor, 1)) { graph.SmoothingMode = SmoothingMode.AntiAlias; control.Region = new Region(roundPath); graph.DrawPath(penBorder, roundPath); } }
Private Sub ControlRegionAndBorder(control As Control, radius As Single, graph As Graphics, borderColor As Color) Using roundPath As GraphicsPath = GetRoundedPath(control.ClientRectangle, radius) Using penBorder As Pen = New Pen(borderColor, 1) graph.SmoothingMode = SmoothingMode.AntiAlias control.Region = New Region(roundPath) graph.DrawPath(penBorder, roundPath) End Using End Using End Sub
Dibujar Rutas – Esquinas del Formulario
private void DrawPath(Rectangle rect, Graphics graph, Color color) { using (GraphicsPath roundPath = GetRoundedPath(rect, borderRadius)) using (Pen penBorder = new Pen(color, 3)) { graph.DrawPath(penBorder, roundPath); } }
Private Sub DrawPath(rectForm As Rectangle, graphics As Graphics, color As Color) Using roundPath As GraphicsPath = GetRoundedPath(rectForm, borderRadius) Using penBorder As Pen = New Pen(color, 3) graphics.DrawPath(penBorder, roundPath) End Using End Using End Sub
Obtener Colores de los Limites Exteriores del Formulario
private struct FormBoundsColors { public Color TopLeftColor; public Color TopRightColor; public Color BottomLeftColor; public Color BottomRightColor; } private FormBoundsColors GetFormBoundsColors() { var fbColor = new FormBoundsColors(); using (var bmp = new Bitmap(1, 1)) using (Graphics graph = Graphics.FromImage(bmp)) { Rectangle rectBmp = new Rectangle(0, 0, 1, 1); //Top Left rectBmp.X = this.Bounds.X - 1; rectBmp.Y = this.Bounds.Y; graph.CopyFromScreen(rectBmp.Location, Point.Empty, rectBmp.Size); fbColor.TopLeftColor = bmp.GetPixel(0, 0); //Top Right rectBmp.X = this.Bounds.Right; rectBmp.Y = this.Bounds.Y; graph.CopyFromScreen(rectBmp.Location, Point.Empty, rectBmp.Size); fbColor.TopRightColor = bmp.GetPixel(0, 0); //Bottom Left rectBmp.X = this.Bounds.X; rectBmp.Y = this.Bounds.Bottom; graph.CopyFromScreen(rectBmp.Location, Point.Empty, rectBmp.Size); fbColor.BottomLeftColor = bmp.GetPixel(0, 0); //Bottom Right rectBmp.X = this.Bounds.Right; rectBmp.Y = this.Bounds.Bottom; graph.CopyFromScreen(rectBmp.Location, Point.Empty, rectBmp.Size); fbColor.BottomRightColor = bmp.GetPixel(0, 0); } return fbColor; }
Private Structure FormBoundsColors Public TopLeftColor As Color Public TopRightColor As Color Public BottomLeftColor As Color Public BottomRightColor As Color End Structure Private Function GetFormBoundsColors() As FormBoundsColors Dim fbColor = New FormBoundsColors() Using bmp = New Bitmap(1, 1) Using graph As Graphics = Graphics.FromImage(bmp) Dim rectBmp As New Rectangle(0, 0, 1, 1) 'Top Left rectBmp.X = Me.Bounds.X - 1 rectBmp.Y = Me.Bounds.Y graph.CopyFromScreen(rectBmp.Location, Point.Empty, rectBmp.Size) fbColor.TopLeftColor = bmp.GetPixel(0, 0) 'Top Right rectBmp.X = Me.Bounds.Right rectBmp.Y = Me.Bounds.Y graph.CopyFromScreen(rectBmp.Location, Point.Empty, rectBmp.Size) fbColor.TopRightColor = bmp.GetPixel(0, 0) 'Bottom Left rectBmp.X = Me.Bounds.X rectBmp.Y = Me.Bounds.Bottom graph.CopyFromScreen(rectBmp.Location, Point.Empty, rectBmp.Size) fbColor.BottomLeftColor = bmp.GetPixel(0, 0) 'Bottom Right rectBmp.X = Me.Bounds.Right rectBmp.Y = Me.Bounds.Bottom graph.CopyFromScreen(rectBmp.Location, Point.Empty, rectBmp.Size) fbColor.BottomRightColor = bmp.GetPixel(0, 0) End Using End Using Return fbColor End Function
Métodos de Evento
Establecer Región Redondeado y Dibujar Borde + Suavizado (Formulario)
private void Form1_Paint(object sender, PaintEventArgs e) { //-> SMOOTH OUTER BORDER e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; Rectangle rectForm = this.ClientRectangle; int mWidht = rectForm.Width / 2; int mHeight = rectForm.Height / 2; var fbColors = GetFormBoundsColors(); //Top Left DrawPath(rectForm, e.Graphics, fbColors.TopLeftColor); //Top Right Rectangle rectTopRight = new Rectangle(mWidht, rectForm.Y, mWidht, mHeight); DrawPath(rectTopRight, e.Graphics, fbColors.TopRightColor); //Bottom Left Rectangle rectBottomLeft = new Rectangle(rectForm.X, rectForm.X + mHeight, mWidht, mHeight); DrawPath(rectBottomLeft, e.Graphics, fbColors.BottomLeftColor); //Bottom Right Rectangle rectBottomRight = new Rectangle(mWidht, rectForm.Y + mHeight, mWidht, mHeight); DrawPath(rectBottomRight, e.Graphics, fbColors.BottomRightColor); //-> SET ROUNDED REGION AND BORDER FormRegionAndBorder(this, borderRadius, e.Graphics, borderColor, borderSize); }
Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles MyBase.Paint '-> Smooth Outer Border e.Graphics.SmoothingMode = SmoothingMode.AntiAlias Dim rectForm As Rectangle = Me.ClientRectangle Dim mWidht As Integer = rectForm.Width / 2 Dim mHeight As Integer = rectForm.Height / 2 Dim fbColor = GetFormBoundsColors() 'Top Left DrawPath(rectForm, e.Graphics, fbColor.TopLeftColor) 'Top Right Dim rectTopRight As New Rectangle(rectForm.Right - mWidht, rectForm.Y, mWidht, mHeight) DrawPath(rectTopRight, e.Graphics, fbColor.TopRightColor) 'Bottom Left Dim rectBottomLeft As New Rectangle(rectForm.X, rectForm.Bottom - mHeight, mWidht, mHeight) DrawPath(rectBottomLeft, e.Graphics, fbColor.BottomLeftColor) 'Bottom Right Dim rectBottomRight As New Rectangle(rectForm.Right - mWidht, rectForm.Bottom - mHeight, mWidht, mHeight) DrawPath(rectBottomRight, e.Graphics, fbColor.BottomRightColor) '-> Set Rounded Region and Border FormRegionAndBorder(Me, borderRadius, e.Graphics, borderColor, borderSize) End Sub
Establecer Región Redondeado y Dibujar Borde Fino (Panel Contenedor)
private void panelContainer_Paint(object sender, PaintEventArgs e) { ControlRegionAndBorder(panelContainer, borderRadius - (borderSize / 2), e.Graphics, borderColor); }
Private Sub PanelContainer_Paint(sender As Object, e As PaintEventArgs) Handles PanelContainer.Paint ControlRegionAndBorder(PanelContainer, borderRadius - (borderSize / 2.0F), e.Graphics, borderColor) End Sub
Actualizar Región y Borde
private void Form1_ResizeEnd(object sender, EventArgs e) { this.Invalidate(); } private void Form1_SizeChanged(object sender, EventArgs e) { this.Invalidate(); } private void Form1_Activated(object sender, EventArgs e) { this.Invalidate(); }
Private Sub Form1_ResizeEnd(sender As Object, e As EventArgs) Handles MyBase.ResizeEnd Me.Invalidate() End Sub Private Sub Form1_SizeChanged(sender As Object, e As EventArgs) Handles MyBase.SizeChanged Me.Invalidate() End Sub Private Sub Form1_Activated(sender As Object, e As EventArgs) Handles MyBase.Activated Me.Invalidate() End Sub