This document was originally published as an MSDN discussion here on September 3rd, 2009

http://social.msdn.microsoft.com/Forums/vstudio/en-US/0cab1cbd-553c-4ac2-97ec-334a4338484d/code-for-2008-and-2010-versions-of-vbnet-have-you-ever-wanted-an-oval-triangle-a-pentagon-a

and has been reproduced here.

This will allow you all to suggest any changes, however small.

__________________________________________________________________

Saw a post earlier which reminded me about Extension Methods.

This led to creating the following code which acts on the base class CONTROL for ALL controls!!

So??!! You might ask what does it do?

Well using just one line of code you can transform any of your controls
 into the shape you want within the bounds of the original shape of the control.

So if you have a square control like a square button or picturebox, you will get a regular shape.

If the dotted rectangle ( that the control is drawn out within ) is wider than it is tall
 or vice-versa than the regular shape is stretched to fit this shape.

Best explained with an example.

From the PROJECT menu using VB.Net 2008 or a later version select ADD MODULE , type in ShapedControls.Vb  in the NAME box and click on OK.

Then PASTE this code in. 

Option Strict On

Imports System.Runtime.CompilerServices

Module ShapedControls

 Public Const Pi As Double = Math.PI
 Public Const DegreesToRadians As Double = 180 / Pi

 <Extension()> _
 Public Sub Shape(ByVal ctrl As Control, _ 
Optional
ByVal NumberOfSides As Integer = 3, _
Optional
ByVal OffsetAngleInDegrees As Double = 0) If NumberOfSides < 3 Then Throw New Exception("Number of sides can only be 3 or more.") Dim MyAngle As Double = OffsetAngleInDegrees / DegreesToRadians Dim radius1 As Integer = ctrl.Height \ 2 Dim radius2 As Integer = ctrl.Width \ 2 Dim xInt, yInt As Integer Dim xDoub, yDoub As Double Dim MyPath As New Drawing2D.GraphicsPath For angle As Double = MyAngle To ((2 * Pi) + MyAngle) Step ((2 * Pi) / NumberOfSides) xDoub = radius2 * Math.Cos(angle) + radius2 yDoub = radius1 * Math.Sin(angle) + radius1 xInt = CInt(Int(xDoub)) yInt = CInt(Int(yDoub)) MyPath.AddLine(New Point(xInt, yInt), New Point(xInt, yInt)) Next MyPath.CloseFigure() ctrl.Region = New Region(MyPath) MyPath.Dispose() End Sub End Module

 

Have you done that? Good.

Now paste this in as your FORM code and run it.

Option Strict On
Public Class Form1

 Dim P1, P2 As New PictureBox

 Private Sub Form1_Load(ByVal sender As System.Object, _ 
ByVal e As System.EventArgs) Handles MyBase.Load Me.WindowState = FormWindowState.Maximized P1.Size = New Size(300, 300) P1.Location = New Point(20, 20) P1.BackColor = Color.LightBlue P1.Shape(4) Me.Controls.Add(P1) P2.Size = New Size(300, 300) P2.Location = New Point(350, 20) P2.BackColor = Color.Yellow P2.Shape(8, 0) Me.Controls.Add(P2) Dim Btn1 As New Button Btn1.Location = New Point(Me.Width \ 2, Me.Height \ 2) Btn1.Size = New Size(300, 150) Btn1.TextAlign = ContentAlignment.MiddleCenter Btn1.Text = "Hi!!" Btn1.Font = New Font("Arial", 30, FontStyle.Underline) Btn1.BackColor = Color.Black Btn1.ForeColor = Color.White Btn1.Shape(6, 30) AddHandler Btn1.Click, AddressOf Btn1Click Me.Controls.Add(Btn1) End Sub Private Sub Btn1Click(ByVal sender As System.Object, ByVal e As System.EventArgs) MessageBox.Show("Hi there!!") Dim ofd As New OpenFileDialog ofd.Filter = "Picture files|*.jpg;*.bmp;*.png" ofd.InitialDirectory = My.Computer.FileSystem.SpecialDirectories.MyPictures Dim result As DialogResult = ofd.ShowDialog P1.BackgroundImageLayout = ImageLayout.Zoom If result = Windows.Forms.DialogResult.OK Then P1.BackgroundImage = Image.FromFile(ofd.FileName) End If End Sub End Class

The SHAPE extension for all controls takes two optional parameters or arguments.

E.g:

Button1.Shape(6,0)


means 6 sides with an offset angle of zero degrees.

Enjoy!!

Oh, and the start point appears to be the right hand point on a circle as if the 0 degrees is EAST on a compass

Please bear this in mind.

As the parameters are OPTIONAL you could say.

Button1.Shape()

or

Button1.Shape(6, 30)

or

Button1.Shape(, 30)

as it defaults to 3 sides ( the minimum ).

 

<edit on 14th July, 2010> With a version of the code below this post the minimum is now 2 sides.

If the containing area is square you end up with a circle, otherwise you will get a pointed oval. </edit>

The above example creates two shaped PictureBoxes and a stretched hexagon shaped button!!

Like this where the first two shapes are PictureBoxes and the hexagon is a Button







_______________________________________________________________________________________________________________________________________________________________


Around September 7th, 2009 we tweaked the code to allow for two-sided shapes.>>

Have added to the original code so you can now have controls in the shape of a pointed oval

For now this can only be horizontal or vertical.

If the control size is square such as 300 X 300 then the control ends up as a circle shape. 

Here is the updated module code.

Option Strict On

Imports System.Runtime.CompilerServices

Module ShapedControls

    Public Const Pi As Double = Math.PI
    Public Const DegreesToRadians As Double = 180 / Pi


    <Extension()> _
    Public Sub Shape(ByVal ctrl As Control, _ 
Optional ByVal NumberOfSides As Integer = 3, Optional ByVal OffsetAngleInDegrees As Double = 0) If NumberOfSides < 2 Then Throw New Exception("Number of sides can only be 2 or more.") Dim MyPath As New Drawing2D.GraphicsPath Dim MyAngle As Double = OffsetAngleInDegrees / DegreesToRadians If NumberOfSides = 2 Then Dim MyPoints() As Point Dim MyPointsList As New List(Of Point) If ctrl.Width = ctrl.Height Then MyPath.AddEllipse(New Rectangle(0, 0, ctrl.Width, ctrl.Height)) ElseIf ctrl.Width > ctrl.Height Then MyPointsList.Add(New Point(0, ctrl.Height \ 2)) MyPointsList.Add(New Point(ctrl.Width \ 2, 0)) MyPointsList.Add(New Point(ctrl.Width, ctrl.Height \ 2)) MyPoints = MyPointsList.ToArray MyPath.AddCurve(MyPoints) MyPointsList.Clear() MyPointsList.Add(New Point(ctrl.Width, ctrl.Height \ 2)) MyPointsList.Add(New Point(ctrl.Width \ 2, ctrl.Height)) MyPointsList.Add(New Point(0, ctrl.Height \ 2)) MyPoints = MyPointsList.ToArray MyPath.AddCurve(MyPoints) MyPointsList.Clear() ElseIf ctrl.Width < ctrl.Height Then MyPointsList.Add(New Point(ctrl.Width \ 2, 0)) MyPointsList.Add(New Point(ctrl.Width, ctrl.Height \ 2)) MyPointsList.Add(New Point(ctrl.Width \ 2, ctrl.Height)) MyPoints = MyPointsList.ToArray MyPath.AddCurve(MyPoints) MyPointsList.Clear() MyPointsList.Add(New Point(ctrl.Width \ 2, ctrl.Height)) MyPointsList.Add(New Point(0, ctrl.Height \ 2)) MyPointsList.Add(New Point(ctrl.Width \ 2, 0)) MyPoints = MyPointsList.ToArray MyPath.AddCurve(MyPoints) MyPointsList.Clear() End If End If Dim radius1 As Integer = ctrl.Height \ 2 Dim radius2 As Integer = ctrl.Width \ 2 Dim xInt, yInt As Integer Dim xDoub, yDoub As Double For angle As Double = MyAngle To ((2 * Pi) + MyAngle) Step ((2 * Pi) / NumberOfSides) xDoub = radius2 * Math.Cos(angle) + radius2 yDoub = radius1 * Math.Sin(angle) + radius1 xInt = CInt(Int(xDoub)) yInt = CInt(Int(yDoub)) MyPath.AddLine(New Point(xInt, yInt), New Point(xInt, yInt)) Next MyPath.CloseFigure() ctrl.Region = New Region(MyPath) MyPath.Dispose() End Sub End Module


Here is some FORM code which demonstrates it and a screenshot.


Option Strict On
Public Class Form1

    Dim P1, P2 As New PictureBox

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Me.WindowState = FormWindowState.Maximized
        P1.Size = New Size(300, 300)
        P1.Location = New Point(20, 20)
        P1.BackColor = Color.LightBlue
        P1.Shape(4)
        Me.Controls.Add(P1)

        P2.Size = New Size(300, 300)
        P2.Location = New Point(350, 20)
        P2.BackColor = Color.Yellow
        P2.Shape(8, 0)
        Me.Controls.Add(P2)

        Dim Btn1 As New Button
        Btn1.Location = New Point(Me.Width \ 4, Me.Height \ 2)
        Btn1.Size = New Size(300, 200)
        Btn1.TextAlign = ContentAlignment.MiddleCenter
        Btn1.Text = "Hi!!"
        Btn1.Font = New Font("Arial", 30, FontStyle.Underline)
        Btn1.BackColor = Color.Black
        Btn1.ForeColor = Color.White
        Btn1.Shape(2)
        AddHandler Btn1.Click, AddressOf Btn1Click
        Me.Controls.Add(Btn1)

    End Sub

    Private Sub Btn1Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

        MessageBox.Show("Hi there!!")
        Dim ofd As New OpenFileDialog
        ofd.Filter = "Picture files|*.jpg;*.bmp;*.png"
        ofd.InitialDirectory = My.Computer.FileSystem.SpecialDirectories.MyPictures
        Dim result As DialogResult = ofd.ShowDialog
        P1.BackgroundImageLayout = ImageLayout.Zoom

        If result = Windows.Forms.DialogResult.OK Then
            P1.BackgroundImage = Image.FromFile(ofd.FileName)
        End If

    End Sub

End Class





The blacked pointed oval above is a button the other shapes are Pictureboxes


_____________________________________________________________________________________________________________________________________

Around September 10th, 2009 we made a further addition.

Have added even more code so you can make any control
like a button into one of eight triangle shapes as below.

E.G: The buttons in the first picture turn into those in the second picture.  :-)    ;-)    >>


Here is the very short FORM code.

Option Strict On

Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Button1.MakeTriangular(TriangleDirection.Up)
        Button2.MakeTriangular(TriangleDirection.Down)
        Button3.MakeTriangular(TriangleDirection.Left)
        Button4.MakeTriangular(TriangleDirection.Right)

        Button5.MakeTriangular(TriangleDirection.TopRight)
        Button6.MakeTriangular(TriangleDirection.TopLeft)
        Button7.MakeTriangular(TriangleDirection.BottomRight)
        Button8.MakeTriangular(TriangleDirection.BottomLeft)

    End Sub

End Class


Here is the updated MODULE code.


Option Strict On

Imports System.Runtime.CompilerServices

Module ShapedControls

    Public Const Pi As Double = Math.PI
    Public Const DegreesToRadians As Double = 180 / Pi

    Public Enum TriangleDirection
        Up
        Right
        Down
        Left
        TopRight
        BottomRight
        BottomLeft
        TopLeft
    End Enum

    <Extension()> _
    Public Sub Shape(ByVal ctrl As Control, _ 
Optional ByVal NumberOfSides As Integer = 3, Optional ByVal OffsetAngleInDegrees As Double = 0) If NumberOfSides < 2 Then Throw New Exception("Number of sides can only be 2 or more.") Dim MyPath As New Drawing2D.GraphicsPath Dim MyAngle As Double = OffsetAngleInDegrees / DegreesToRadians If NumberOfSides = 2 Then Dim MyPoints() As Point Dim MyPointsList As New List(Of Point) If ctrl.Width = ctrl.Height Then MyPath.AddEllipse(New Rectangle(0, 0, ctrl.Width, ctrl.Height)) ElseIf ctrl.Width > ctrl.Height Then MyPointsList.Add(New Point(0, ctrl.Height \ 2)) MyPointsList.Add(New Point(ctrl.Width \ 2, 0)) MyPointsList.Add(New Point(ctrl.Width, ctrl.Height \ 2)) MyPoints = MyPointsList.ToArray MyPath.AddCurve(MyPoints) MyPointsList.Clear() MyPointsList.Add(New Point(ctrl.Width, ctrl.Height \ 2)) MyPointsList.Add(New Point(ctrl.Width \ 2, ctrl.Height)) MyPointsList.Add(New Point(0, ctrl.Height \ 2)) MyPoints = MyPointsList.ToArray MyPath.AddCurve(MyPoints) MyPointsList.Clear() ElseIf ctrl.Width < ctrl.Height Then MyPointsList.Add(New Point(ctrl.Width \ 2, 0)) MyPointsList.Add(New Point(ctrl.Width, ctrl.Height \ 2)) MyPointsList.Add(New Point(ctrl.Width \ 2, ctrl.Height)) MyPoints = MyPointsList.ToArray MyPath.AddCurve(MyPoints) MyPointsList.Clear() MyPointsList.Add(New Point(ctrl.Width \ 2, ctrl.Height)) MyPointsList.Add(New Point(0, ctrl.Height \ 2)) MyPointsList.Add(New Point(ctrl.Width \ 2, 0)) MyPoints = MyPointsList.ToArray MyPath.AddCurve(MyPoints) MyPointsList.Clear() End If End If Dim radius1 As Integer = ctrl.Height \ 2 Dim radius2 As Integer = ctrl.Width \ 2 Dim xInt, yInt As Integer Dim xDoub, yDoub As Double For angle As Double = MyAngle To ((2 * Pi) + MyAngle) Step ((2 * Pi) / NumberOfSides) xDoub = radius2 * Math.Cos(angle) + radius2 yDoub = radius1 * Math.Sin(angle) + radius1 xInt = CInt(Int(xDoub)) yInt = CInt(Int(yDoub)) MyPath.AddLine(New Point(xInt, yInt), New Point(xInt, yInt)) Next MyPath.CloseFigure() ctrl.Region = New Region(MyPath) MyPath.Dispose() End Sub <Extension()> _ Public Sub MakeTriangular(ByVal ctrl As Control, ByVal Triangle_Direction As TriangleDirection) Dim MyPath As New Drawing2D.GraphicsPath Select Case Triangle_Direction Case Is = TriangleDirection.Up MyPath.AddLine(0, ctrl.Height, 0, ctrl.Height) MyPath.AddLine(0, ctrl.Height, ctrl.Width \ 2, 0) MyPath.AddLine(ctrl.Width \ 2, 0, ctrl.Width, ctrl.Height) Case TriangleDirection.Right MyPath.AddLine(0, ctrl.Height, 0, 0) MyPath.AddLine(0, 0, ctrl.Width, ctrl.Height \ 2) Case TriangleDirection.Down MyPath.AddLine(0, 0, ctrl.Width, 0) MyPath.AddLine(ctrl.Width, 0, ctrl.Width \ 2, ctrl.Height) Case TriangleDirection.Left MyPath.AddLine(ctrl.Width, 0, ctrl.Width, ctrl.Height) MyPath.AddLine(ctrl.Width, ctrl.Height, 0, ctrl.Height \ 2) Case TriangleDirection.TopRight MyPath.AddLine(0, 0, ctrl.Width, 0) MyPath.AddLine(ctrl.Width, 0, ctrl.Width, ctrl.Height) Case TriangleDirection.TopLeft MyPath.AddLine(0, 0, ctrl.Width, 0) MyPath.AddLine(ctrl.Width, 0, 0, ctrl.Height) Case TriangleDirection.BottomRight MyPath.AddLine(ctrl.Width, 0, ctrl.Width, ctrl.Height) MyPath.AddLine(ctrl.Width, ctrl.Height, 0, ctrl.Height) Case TriangleDirection.BottomLeft MyPath.AddLine(0, 0, ctrl.Width, ctrl.Height) MyPath.AddLine(ctrl.Width, ctrl.Height, 0, ctrl.Height) End Select MyPath.CloseFigure() ctrl.Region = New Region(MyPath) End Sub End Module

____________________________________________________________________________________________________

Hope you all have as much fun using this code as when writing it.

For star shapes and other stuff please see this thread 

http://social.msdn.microsoft.com/Forums/vstudio/en-US/543c39c0-60ed-4a02-a240-725b4a88ca8e/how-to-make-a-control-such-as-a-picturebox-into-a-star-shape#ab1b98e1-86cb-430f-99b3-a39f90935743