Creating a Rounded Panel Control

Introduction

Occasionally, I get tired of plain old rectangular shaped WinForms applications and decide to add some simple rounded-rectangular-ly shaped elements to change it up. The problem is I have to remember how to make them. And, because it's the holiday season, why not keep it fun and light? For your programming pleasure and amusement, in the spirit of the holidays, here is a simple control that rounds the edges of a panel.

Understanding the Coordinate System for Drawing Arcs with GDI+

Generally, in math classes or if you are a pilot or use maps, you think of the coordinate system with North (or 360° or 0°) as being situated at the top, rotation clockwise through 360°. In a Cartesian system this would situate 0° at the top and 180° at the bottom, representing the y-axis and 90° and 270° situated left and right representing the x-axis. GDI+ does not treat the coordinate system that way, at least as far as drawing arcs is concerned. The coordinate system is rotated 90° to the right, as shown in Figure 1.

Figure 1: The Cartesian coordinate system is oriented titled to the right, so 0° is on the right or x-axis.

Knowing the orientation of significant reference points is important if you are going to draw anything manually with GDI+.

Technically, to draw a rounded corner you must know the radius of your circle. The radius will be the offset distance from any of the rectangle's four edges. For example, a rounded upper-right corner would be start at width-d and 0 where d represents the arcs radius, width-d is the x-coordinate and 0 is the y-coordinate. Because the arc starts in the up position, the starting angle would be 270°; to obtain a quarter-circle, the arc would sweep 90°. (Four corners mean 360° divided by 4, yields 90° for each corner arc.) See Figure 2 for an illustration.

Figure 2: To round a corner, you need x and y and the starting angle and sweep; the upper right corner is 270° and a quarter circle is 90°.

Creating the Rounded-Rectangle Panel Control

To implement the rounded-rectangle effect, you need a graphics path with the lines and arcs that enclose the panel and assign this graphics path to the Region property of the panel. All of this additional code sits tidily packaged up in a class that inherits from Panel and overrides the OnPaint method of the Panel. Listing 1 provides the complete source code listing.

Listing 1: A complete listing for a rounded-rect Panel control.

Imports System
Imports System.Diagnostics
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Drawing
Imports System.Drawing.Drawing2D


Public Class ShapedPanel
   Inherits Panel

   Private pen As Pen = New Pen(_borderColor, penWidth)
   Private Shared ReadOnly penWidth As Single = 2.0F

   Public Sub New()

   End Sub

   Private _borderColor As Color = Color.White
   <Browsable(True)> _
   Public Property BorderColor() As Color
      Get
         Return _borderColor
      End Get
      Set(ByVal Value As Color)
         _borderColor = Value
         pen = New Pen(_borderColor, penWidth)
         Invalidate()
      End Set
   End Property

   Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
      MyBase.OnPaint(e)
      ExtendedDraw(e)
      DrawBorder(e.Graphics)
   End Sub

   Private _edge As Integer = 50
   <Browsable(True)> _
   Public Property Edge() As Integer
      Get
         Return _edge
      End Get
      Set(ByVal Value As Integer)
         _edge = Value
         Invalidate()
      End Set
   End Property


   Private Function GetLeftUpper(ByVal e As Integer) As Rectangle
      Return New Rectangle(0, 0, e, e)
   End Function

   Private Function GetRightUpper(ByVal e As Integer) As Rectangle
      Return New Rectangle(Width - e, 0, e, e)
   End Function

   Private Function GetRightLower(ByVal e As Integer) As Rectangle
      Return New Rectangle(Width - e, Height - e, e, e)
   End Function

   Private Function GetLeftLower(ByVal e As Integer) As Rectangle
      Return New Rectangle(0, Height - e, e, e)
   End Function

   Private Sub ExtendedDraw(ByVal e As PaintEventArgs)
      e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
      Dim path As GraphicsPath = New GraphicsPath()
      path.StartFigure()
      path.StartFigure()
      path.AddArc(GetLeftUpper(Edge), 180, 90)
      path.AddLine(Edge, 0, Width - Edge, 0)
      path.AddArc(GetRightUpper(Edge), 270, 90)
      path.AddLine(Width, Edge, Width, Height - Edge)
      path.AddArc(GetRightLower(Edge), 0, 90)
      path.AddLine(Width - Edge, Height, Edge, Height)
      path.AddArc(GetLeftLower(Edge), 90, 90)
      path.AddLine(0, Height - Edge, 0, Edge)
      path.CloseFigure()
      Region = New Region(path)
   End Sub

   Private Sub DrawSingleBorder(ByVal graphics As Graphics)
      graphics.DrawArc(pen, New Rectangle(0, 0, Edge, Edge), _
                       180, 90)
      graphics.DrawArc(pen, New Rectangle(Width - Edge - 1, -1, _
                       Edge, Edge), 270, 90)
      graphics.DrawArc(pen, New Rectangle(Width - Edge - 1, _
                       Height - Edge - 1, Edge, Edge), 0, 90)
      graphics.DrawArc(pen, New Rectangle(0, Height - Edge - 1, _
                       Edge, Edge), 90, 90)
      graphics.DrawRectangle(pen, 0.0F, 0.0F, CType((Width - 1), _
                             Single), CType((Height - 1), Single))
   End Sub

   Private Sub Draw3DBorder(ByVal graphics As Graphics)
      'TODO Implement 3D border
   End Sub

   Private Sub DrawBorder(ByVal graphics As Graphics)
      DrawSingleBorder(graphics)
   End Sub
End Class

Creating a Rounded Panel Control

The solution originates in OnPaint calling the base OnPaint method to draw the basic Panel and then ExtendedDraw and DrawBorder to complete the effect. ExtendedDraw creates the GraphicsPath that traces the Panel's original bounds and adds enclosing Arcs for the corners. DrawBorder as designed will always draw a border around the Panel. See Figure 3 for the result.

[RoundedPanel3.jpg]

Figure 3: The rounded rectangle with a Lavender background and white border on a CornFlowerBlue form.

Because the GraphicsPath is assigned to the Panel's Region (aka clipping region), the actual boundaries of the Panel are constrained by the new region. This means if you assign a click event to the Panel and click in the missing corners, the Panel will not receive or respond to the event; that is, the Panel's actually region is constrained by the GraphicsPath, not just its visual appearance.

Summary

Custom shaped controls can add some visual interest to you applications. You need to know the orientation of the drawing reference system for GDI+ and then it's basically lines, arcs, shapes, and graphics used with a GraphicsPath object and the control's clipping region. You aren't required to use inheritance to create shaped controls, but it does make them easier to reuse.

About the Author

Paul Kimmel is a freelance writer for Developer.com and CodeGuru.com. He is the founder of Software Conceptions, Inc, founded in 1990. Paul Kimmel is architect for EDS, an HP Company. You may contact him about article questions at pkimmel@softconcepts.com.

Check out Paul's most recent books, LINQ Unleashed and Teach Yourself the ADO.NET Entity Framework in 24 Hours (coming Spring 2009).

Copyright © by Paul T. Kimmel. All Rights Reserved.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

  • With 81% of employees using their phones at work, companies have stopped asking: "Is corporate data leaking from personal devices?" and started asking: "How do we effectively prevent corporate data from leaking from personal devices?" The answer has not been simple. ZixOne raises the bar on BYOD security by not allowing email data to reside on the device. In addition, Zix allows employees to maintain complete control of their personal device, therefore satisfying privacy demands of valued employees and the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds