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 SubMé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 SubMinimizar 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 PropertyMé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 FunctionEstablecer 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 SubEstablecer 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 SubDibujar 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 SubObtener 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 FunctionMé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 SubEstablecer 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 SubActualizar 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