Question Smooth round borderless form corner

Zexor

Well-known member
Joined
Nov 28, 2008
Messages
520
Programming Experience
3-5
I am trying to give my borderless form round corners. i used Drawing2D.GraphicsPath() and draw a path with 4 arcs around the form then set the region of the form to it. it came out with somewhat jagged corner. And the left 2 corners just doesnt look the same as the right 2. :confused: Is there a way to make them smoother like the normal bordered form? Maybe i need to draw a border around the borderless form? hehe :D
 
Last edited:
Hi Zexor,

I guess you realize that drawing a border round the form doesn't help much. Even if you set Graphics.SmoothingMode to HighQuality, the anti-alias pixels on the outside of the curves will be clipped off by the region. So the corners will look just as rough.

What you need to do is to define a GraphicsPath with round corners which is 1 pixel inside the edges of the actual form. Use a screen capture capture method (such as Graphics.CopyFromScreen) to grab the full area of the form. Then use FillPath and/or DrawPath to paint the form surface onto the captured bitmap, with the SmoothingMode set to HighQuality. Since there is a 1-pixel margin of "background" all around the edges of the path, what you draw will be antialised into the background and you will have smooth corners. Obviously, you need to repeat this process when the form is dragged to a new position.

I hope this helps,
VicJ
 
hello VicJ

Hi Zexor,

I guess you realize that drawing a border round the form doesn't help much. Even if you set Graphics.SmoothingMode to HighQuality, the anti-alias pixels on the outside of the curves will be clipped off by the region. So the corners will look just as rough.

What you need to do is to define a GraphicsPath with round corners which is 1 pixel inside the edges of the actual form. Use a screen capture capture method (such as Graphics.CopyFromScreen) to grab the full area of the form. Then use FillPath and/or DrawPath to paint the form surface onto the captured bitmap, with the SmoothingMode set to HighQuality. Since there is a 1-pixel margin of "background" all around the edges of the path, what you draw will be antialised into the background and you will have smooth corners. Obviously, you need to repeat this process when the form is dragged to a new position.

I hope this helps,
VicJ

Can care to elaborate... maybe with some example code... :)
I'm quite there... but not there yet...
Public Sub CutForm(ByVal Target As Form, ByVal Corner As Integer, ByVal e As PaintEventArgs)
        Try
            Dim Path As New GraphicsPath()
            Dim Border As New GraphicsPath()
            Dim Pen As New Pen(Color.Black, 1)
            Dim Offset As Integer = 1
            Dim Margin As Integer = 0


            Dim myGraphics As Graphics = Target.CreateGraphics()
            Dim s As Size = Target.Size
            memoryImage = Nothing
            memoryImage = New Bitmap(s.Width, s.Height, myGraphics)
            Dim memoryGraphics As Graphics = Graphics.FromImage(memoryImage)


            Path.StartFigure()
            Path.AddArc(New Rectangle(Offset, Offset, Corner, Corner), 180, 90)
            Path.AddLine(Corner, Offset, Target.Width - Margin - Corner, Offset)
            Path.AddArc(New Rectangle(Target.Width - Margin - Corner - Offset, Offset, Corner, Corner), -90, 90)
            Path.AddLine(Target.Width - Margin - Offset, Corner, Target.Width - Margin - Offset, Target.Height - Margin - Corner)
            Path.AddArc(New Rectangle(Target.Width - Margin - Corner - Offset, Target.Height - Margin - Corner - Offset, Corner, Corner), 0, 90)
            Path.AddLine(Target.Width - Corner, Target.Height - Margin - Offset, Corner, Target.Height - Margin - Offset)
            Path.AddArc(New Rectangle(Offset, Target.Height - Margin - Corner - Offset, Corner, Corner), 90, 90)
            Path.AddLine(Offset, Target.Height - Margin - Corner, Offset, Corner)
            Path.CloseFigure()
            Target.Region = New Region(Path)
            memoryGraphics.CopyFromScreen(Target.Location.X, Target.Location.Y, 0, 0, s)
            Path.Dispose()


            Offset = 1
            Margin = 1
            Border.StartFigure()
            Border.AddArc(New Rectangle(Offset, Offset, Corner, Corner), 180, 90)
            Border.AddLine(Corner, Offset, Target.Width - Margin - Corner, Offset)
            Border.AddArc(New Rectangle(Target.Width - Margin - Corner - Offset, Offset, Corner, Corner), -90, 90)
            Border.AddLine(Target.Width - Margin - Offset, Corner, Target.Width - Margin - Offset, Target.Height - Margin - Corner)
            Border.AddArc(New Rectangle(Target.Width - Margin - Corner - Offset, Target.Height - Margin - Corner - Offset, Corner, Corner), 0, 90)
            Border.AddLine(Target.Width - Corner, Target.Height - Margin - Offset, Corner, Target.Height - Margin - Offset)
            Border.AddArc(New Rectangle(Offset, Target.Height - Margin - Corner - Offset, Corner, Corner), 90, 90)
            Border.AddLine(Offset, Target.Height - Margin - Corner, Offset, Corner)
            Border.CloseFigure()


            memoryGraphics.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
            memoryGraphics.DrawPath(Pen, Border)
            Border.Dispose()
        Catch ex As Exception
            MsgBox("Err : " & ex.Message)
        End Try
    End Sub
 
Last edited by a moderator:
rarva, have you tried something simple like setting the form's BackGround to the same color as the TransparencyKey then simply drawing the form however you want?
Here's a basic example:
Imports System.Drawing

Public Class Form1

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        Me.DoubleBuffered = True
        Me.Size = New Size(300, 300)
        Me.BackColor = Color.Fuchsia
        Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None
        Me.TransparencyKey = Color.Fuchsia
    End Sub

    Protected Overrides Sub OnPaintBackground(e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaintBackground(e)
        With e.Graphics
            .FillEllipse(Brushes.Black, 0, 0, Me.ClientSize.Width, Me.ClientSize.Height)
        End With
    End Sub

End Class
 
JB, the question is not how to shape the form but how to smooth its edges. Both TransparencyKey and Region produce jagged edges on curves and diagonals.

rarva, the method I suggested in post #2 can work well for a static form on an unchanging background. But it's a bit unreliable otherwise. The problem is that you have to hide the drawn border in order to (re)capture the pixels of the background, and it's hard to make that happen automatically.

There's an alternative you might like to try. It involves drawing a partly transparent border over the form's actual border. The border extends outside the main form so a separate form is used to show it. The border form's Opacity setting provides the required transparency. In my view result is better than the undecorated jagged edges (though not as good as you could get with WPF). It looks quite good when the main form has a photographic background image, and it isn't spoiled by a changing background. Here's an example:

VB.NET:
Public Class Form1

    Private borderForm As New Form

    Private Sub Form1_Shown(sender As Object, e As System.EventArgs) Handles Me.Shown
        With Me
            .FormBorderStyle = Windows.Forms.FormBorderStyle.None
            .Region = New Region(RoundedRectangle(.ClientRectangle, 50))
        End With
        With borderForm
            .ShowInTaskbar = False
            .FormBorderStyle = Windows.Forms.FormBorderStyle.None
            .StartPosition = FormStartPosition.Manual
            .BackColor = Color.Black
            .Opacity = 0.25
            Dim r As Rectangle = Me.Bounds
            r.Inflate(2, 2)
            .Bounds = r
            .Region = New Region(RoundedRectangle(.ClientRectangle, 50))
            r = New Rectangle(3, 3, Me.Width - 4, Me.Height - 4)
            .Region.Exclude(RoundedRectangle(r, 48))
            .Show(Me)
        End With
    End Sub

    Private Function RoundedRectangle(rect As RectangleF, diam As Single) As Drawing2D.GraphicsPath
        Dim path As New Drawing2D.GraphicsPath
        path.AddArc(rect.Left, rect.Top, diam, diam, 180, 90)
        path.AddArc(rect.Right - diam, rect.Top, diam, diam, 270, 90)
        path.AddArc(rect.Right - diam, rect.Bottom - diam, diam, diam, 0, 90)
        path.AddArc(rect.Left, rect.Bottom - diam, diam, diam, 90, 90)
        path.CloseFigure()
        Return path
    End Function

    Private Sub Form1_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
        Dim r As New Rectangle(1, 1, Me.Width - 2, Me.Height - 2)
        Dim path As Drawing2D.GraphicsPath = RoundedRectangle(r, 48)
        Using pn As New Pen(Color.Black, 2)
            e.Graphics.DrawPath(pn, path)
        End Using
    End Sub

End Class

Note that the argument is important in the .Show(Me) statement. Also, there's no need to add the sides in the RoundedRectangle function, because CloseFigure automatically connects up the corners. If you change the size or position of the main form in code, adjust the bounds of the border form in the same way as in the Form_Shown sub.

Vic
 
Back
Top