Search code examples
.netvb.netuser-controlspaintrounded-corners

Issue Drawing Rounded Corners On Custom Control


I am trying to create a custom control that resembles a long rectangle with rounded corners and a border. It will also contain some text, an icon and so forth, but I am having issues with the manual drawing of the control when added programmatically to my form.

The code looks like so:

Option Explicit On
Option Strict On

Imports System.Runtime.InteropServices
Imports Transitions
Imports System.Drawing.Drawing2D

Public Class AlertPanel

    Private m_Radius As Integer
    Private m_BorderWidth As Integer
    Private m_AlertType As AlertType
    Private m_Icon As Image

    ''' <summary>
    ''' Indicates a Radius of the control's corners
    ''' </summary>
    ''' <returns>The corner Radius.</returns>
    Public Property Radius As Integer
        Get
            Return m_Radius
        End Get
        Set(value As Integer)
            m_Radius = value
        End Set
    End Property

    ''' <summary>
    ''' Indicates the width to draw the outer border of the control.
    ''' </summary>
    ''' <returns>The border width.</returns>
    Public Property BorderWidth As Integer
        Get
            Return m_BorderWidth
        End Get
        Set(value As Integer)
            m_BorderWidth = value
        End Set
    End Property

    ''' <summary>
    ''' Indicates the type of Alert for the control.
    ''' </summary>
    ''' <returns>The Alert type.</returns>
    Public Property AlertType As AlertType
        Get
            Return m_AlertType
        End Get
        Set(value As AlertType)
            m_AlertType = value
        End Set
    End Property

    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()

        ' Add any initialization after the InitializeComponent() call.

    End Sub

    Private Sub AlertPanel_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
        Dim rect As Rectangle = Me.ClientRectangle 'Drawing Rounded Rectangle
        rect.X = rect.X + 1
        rect.Y = rect.Y + 1
        rect.Width -= 2
        rect.Height -= 2

        Using bb As GraphicsPath = GetPath(rect, Radius)
            'Draw the background
            Using br As Brush = New SolidBrush(BackColor)
                e.Graphics.SmoothingMode = SmoothingMode.HighQuality
                e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic
                e.Graphics.FillPath(br, bb)
            End Using
            'Draw the border
            Using br As Brush = New SolidBrush(ForeColor)
                rect.Inflate(-1, -1)
                e.Graphics.SmoothingMode = SmoothingMode.HighQuality
                e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic
                e.Graphics.DrawPath(New Pen(br, BorderWidth), bb)
            End Using
        End Using
    End Sub

    Protected Function GetPath(ByVal rc As Rectangle, ByVal r As Int32) As GraphicsPath
        Dim x As Int32 = rc.X, y As Int32 = rc.Y, w As Int32 = rc.Width, h As Int32 = rc.Height
        r = r << 1
        Dim path As GraphicsPath = New GraphicsPath()
        If r > 0 Then
            If (r > h) Then r = h
            If (r > w) Then r = w
            path.AddArc(x, y, r, r, 180, 90)
            path.AddArc(x + w - r, y, r, r, 270, 90)
            path.AddArc(x + w - r, y + h - r, r, r, 0, 90)
            path.AddArc(x, y + h - r, r, r, 90, 90)
            path.CloseFigure()
        Else
            path.AddRectangle(rc)
        End If
        Return path
    End Function

End Class

Then I call it like this:

Dim a As New AlertPanel
            With a
                .Size = New Size(400, 60)
                .Location = New Point(100, 200)
                .AlertType = AlertType.Major
                .ForeColor = Color.Black
                .BorderWidth = 1
                .BackColor = Color.IndianRed
                .Radius = 10
            End With
            Me.Controls.Add(a)

And I am left with this:

enter image description here

UPDATE

After some edits to my code as per Plutonix below, I now have the following results:

enter image description here

FINAL UPDATE

Final result looks like below. Many thanks to Plutonix for all the help!

enter image description here


Solution

  • I am trying to create a custom control You didnt say whether this was subclassing a control (like Panel) or was built as a UserControl. I am guessing the latter based on InitializeComponent().

    The main thing seems to be that the standard Border for the UserControl is still being painted. Add this to turn it off:

    Public Sub New()
        MyBase.BorderStyle = Windows.Forms.BorderStyle.None
        ...
    

    You may want to hide that property so the end user cant turn it back on in Properties.

    Also, with wider borders you will find that the bottom border is being clipped by one pixel (somewhat apparent in your image - the top horizontal appears thicker than the bottom one). Add this to your GetPath method:

    h As Int32 = rc.Height - 1
    

    Result:

    enter image description here

    Also, BackColor is inherited and applies to the entire client area, which remains a rectangle. You'll probably need to replace that as well. Leave the actual BackColor as Transparent, maybe hiding it that property from the user as well, and use a new FillColor property:

    ' in the paint event
    Using br As Brush = New SolidBrush(FillColor)
       ...
    

    Using a FillColor property and forcing BackColor to Transparent:

    enter image description here