WindowsDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


AddThis Social Bookmark Button

VB .NET Custom Controls

by Budi Kurniawan
06/03/2002

Normally when you develop a .NET Windows application, you will use controls from the System.Windows.Forms namespace. You have a wide variety of controls available, from simple controls such as Label and TextBox, to controls that are richer and more complex, such as MonthCalendar and ColorDialog. While these controls are good enough for most Windows application you need to build, sometimes you need to create a different control that you cannot find in the "box." This article shows you how to write a custom control in VB.NET, especially if you need to provide your own graphical user interface. The code is the modified version of the control I built for my C# article.

If you think that you will never have to roll up your sleeves to write your own Windows control, think again. There are so many different applications that have been built, are being built, and will be built. No two applications are the same, so there will always be a need for for different controls, as well. For example, you might be developing an XML editor application that requires a text editor with line numbering. You might think at first that you can use the RichTextBox class, but you soon find out after reading the list of its class members that it does not provide line numbers. Or, for another example, you might need a round button.

If you are lucky, you can find someone who has written a control similar to the one you need. If not, you have to develop you own custom control. It turns out it's not that hard. In writing your custom controls, you can use other existing controls or extend the Control or UserControl classes. Incorporating existing controls saves you the trouble of providing your own user interface. Extending the Control or UserControl class means that you have to override the OnPaint method to draw your own graphical user interface.

This article shows you a custom control that inherits the UserControl class, which itself is derived from the Control class. Therefore, you should be familiar with these two classes before you jump into coding.

Related Reading

Programming Visual Basic for the Palm OS
By Matthew Holmes, Patrick Burton, Roger Knoell

The Control class is important because it is the parent class of Windows visual components. Your custom class will be a child class of the Control class, too; however, your custom controls don't normally inherit directly from the Control class. Instead, you extend the UserControl class. The first two sections of this article discuss the Control and UserControl classes. They are followed by the section that explains how to build your own custom control: "The RoundButton Control."

The Control Class

The Control class provides basic functionality required by classes that display information to the Windows application user. This class handles user input through the keyboard and the mouse, as well as message routing and security. More importantly, the Control class defines the bounds of a control (its position and size), although it does not implement painting.

Windows forms controls use "ambient properties" so child controls can appear similar to their surrounding environment. An ambient property is one that by default is retrieved from the parent control. If the control does not have a parent and the property is not set, the control tries to determine the value of the ambient property through the Site property. If the control is not sited, if the site does not support ambient properties, or if the property is not set on the AmbientProperties object, the control uses its own default values. Typically, an ambient property represents a characteristic of a control, such as BackColor, that is communicated to a child control. For example, a Button will have the same BackColor as its parent form, by default.

A number of the Control class' properties, methods, and events are carried through to its child classes without any change.

The Control Class' Properties

The following are some of the Control class' most important properties:

BackColor
The background color of the control, represented by a System.Drawing.Color object. You can programmatically assign a System.Drawing.Color object to this property using code like the following:

control.BackColor = System.Drawing.Color.Red
Enabled
A Boolean that indicates whether or not the control is enabled. The default value is True.
Location
The position of the top-left corner of the control in its container, represented by a System.Drawing.Point object.
Name
The name of the control.
Parent
Returns a reference to the container or parent of the control. For example, the parent of a control that is added to a form is the form itself. The following code, for instance, changes the title bar of the form to which Button1 was added to "Thank you.":

Button1.Parent.Text = "Thank you."
Size
The size of the control, as represented by a System.Drawing.Size object.
Text
The String that is associated with the control. For example, in a Label control, the Text property is the String that appears on the label body.

The Control Class' Methods

Some of the Control class' frequently used methods are:

BringToFront
Fully displays the control, if it was underneath some other control. In other words, this method shows the entire control.
CreateGraphics
Obtains the System.Drawing.Graphics object of the control on which you can draw using the various methods of the System.Drawing.Graphics class. For instance, the following code obtains the Graphics object of a button control called Button1, and then draws a diagonal green line across the button's body:

Imports System.Drawing

Dim graphics As Graphics = Button1.CreateGraphics
Dim pen As Pen = New Pen(Color.Green)
graphics.DrawLine(pen, 0, 0, _
  Button1.Size.Width, Button1.Size.Height)

However, drawing on a control this way does not result in "permanent" drawings. When the control is repainted, as it is when the form containing the control is resized, the graphics will disappear. The section "The RoundButton Control" below explains how to make the user interface redraw every time the control is repainted.

Focus
Gives the focus to the control, making it the active control.
Hide
Set the control's Visible property to False, so that it is not shown.
GetNextControl
Returns the next control in the tab order.
OnXXX
Raises the XXX event, where XXX includes Click, ControlAdded, ControlRemoved, DoubleClick, DragDrop, DragEnter, DragLeave, DragOver, Enter, GotFocus, KeyDown, KeyPress, KeyUp, LostFocus, MouseDown, MouseEnter, MouseHover, MouseLeave, MouseMove, MouseUp, Move, Paint, Resize, and TextChanged. For example, calling the OnClick method of the control will trigger its Click event.
Show
Sets the control's Visible property to True so that the control is shown.

The UserControl Class

The UserControl class provides an empty control that can be used to create other controls. It is an indirect child of the Control class. The object hierarchy of this control is as follows.

System.Object
  System.MarshalByRefObject
    System.ComponentModel.Component
      System.Windows.Forms.Control
        System.Windows.Forms.ScrollableControl
          System.Windows.Forms.ContainerControl
            System.Windows.Forms.UserControl

The UserControl class inherits all of the standard positioning and mnemonic-handling code from the ContainerControl class. This code is needed in a user control.

The RoundButton Control

Having the two classes, Control and UserControl, it is very easy to develop a custom Windows control. Your custom control class inherits the UserControl class and, because the UserControl class is also a descendant of the Control class, your custom control will also inherit all of the useful methods, properties, and events from the Control class. Event handling, for example, is automatically inherent in your custom control, thanks to the Control class.

One particularly important thing when developing a custom control is how you draw the user interface. Whatever shape your custom control has, be aware that the control is repainted occasionally. Therefore, the user interface must be redrawn whenever your custom control is repainted. Considering that the Control class' OnPaint method is called every time the control is repainted, overriding this method with a new OnPaint method that draws your custom control's user interface will ensure that your custom control has a permanent look.

The code in Example 1 presents a custom control called RoundButton. Figure 1 shows the RoundButton custom control on a form, the code for which is given in Listing 2. Basically, all you need to do is override the OnPaint method. The system passes a PaintEventArgs object to this method, from which you can obtain the control's System.Drawing.Graphics object. You can then use its methods to draw the user interface.

Listing 1. The RoundButton Control

Imports System.Windows.Forms
Imports System.Drawing

Public Class RoundButton : Inherits UserControl


  Public BackgroundColor As Color = Color.Blue
  Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)

    Dim graphics As Graphics = e.Graphics
    Dim penWidth As Integer = 4
    Dim pen As Pen = New Pen(Color.Black, 4)

    Dim fontHeight As Integer = 10
    Dim font As Font = New Font("Arial", fontHeight)

    Dim brush As SolidBrush = New SolidBrush(BackgroundColor)
    graphics.FillEllipse(brush, 0, 0, Width, Height)
    Dim textBrush As SolidBrush = New SolidBrush(Color.Black)

    graphics.DrawEllipse(pen, CInt(penWidth / 2), _
      CInt(penWidth / 2), Width - penWidth, Height - penWidth)

    graphics.DrawString(Text, font, textBrush, penWidth, _
      Height / 2 - fontHeight)
  End Sub
End Class

The code in Example 1 is a bit of a surprise, isn't it? It's too simple to be true. Your class has only one method: OnPaint. In a nutshell, this method passes a PaintEventArgs object, from which a System.Drawing.Graphics object can be obtained. This Graphics object represents the draw area of your custom control. Draw whatever you want on this Graphics object and it will be displayed as the user interface of your custom control.

In Windows programming, you need a pen, and sometimes a brush, to draw a shape. To write text, you will also need a font. The following code in the OnPaint method creates a System.Drawing.Pen object with a tip that has a width of 4.


      Dim penWidth As Integer = 4
      Dim pen As Pen = New Pen(Color.Black, 4)

It then creates a Arial Font object with a height of 10.

  Dim fontHeight As Integer = 10
      Dim font As Font = New Font("Arial", fontHeight)

The last bit of preparation is to instantiate a SolidBrush object having the same color as the value of the backgroundColor field.

      Dim brush As SolidBrush = New SolidBrush(backgroundColor)

Now you can start drawing. For the base, you use the Graphics class' FillEllipse method. The width and height of the circle are the same as the width and height of the control.

graphics.FillEllipse(brush, 0, 0, Width, Height)

Then, you instantiate another brush that you will use to draw text.

Dim textBrush As SolidBrush = New SolidBrush(Color.Black)

For the circle, you use the DrawEllipse method of the Graphics class.

      graphics.DrawEllipse(pen, Cint(penWidth/2), _
        CInt(penWidth/2), Width - penWidth, Height - penWidth)

Finally, you draw the text on the Graphics object using the DrawString method.

      graphics.DrawString(Text, font, textBrush, penWidth, _
        Height / 2 - fontHeight)

The RoundButton control is shown in Figure 1.


Figure 1. The RoundButton control embedded in a form.

Now, compile your control into a .DLL file and it's ready for use.

The code in Example 2 presents a Windows form called MyForm that uses the RoundButton control.

Listing 2: Using the RoundButton control

Public Class MyForm
  Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

  Private WithEvents roundButton As RoundButton
  Public Sub New()
    MyBase.New()

    'This call is required by the Windows Form Designer.
    InitializeComponent()

    'Add any initialization after the InitializeComponent() call

  End Sub

  'Form overrides dispose to clean up the component list.
  Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
    If disposing Then
      If Not (components Is Nothing) Then
        components.Dispose()
      End If
    End If
    MyBase.Dispose(disposing)
  End Sub

  'Required by the Windows Form Designer
  Private components As System.ComponentModel.IContainer

  'NOTE: The following procedure is required by the Windows Form Designer
  'It can be modified using the Windows Form Designer.  
  'Do not modify it using the code editor.
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    '
    'MyForm
    '
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
    Me.ClientSize = New System.Drawing.Size(292, 273)
    Me.Name = "MyForm"
    Me.Text = "Using Custom Control"

    roundButton = New RoundButton()
    AddHandler roundButton.Click, AddressOf roundButton_Click
    roundButton.Text = "Click Here!"
    roundButton.BackgroundColor = System.Drawing.Color.White
    roundButton.Size = New System.Drawing.Size(80, 80)
    roundButton.Location = New System.Drawing.Point(100, 30)
    Me.Controls.Add(roundButton)

  End Sub

#End Region

  Private Sub roundButton_Click(ByVal source As Object, ByVal e As EventArgs)
    MessageBox.Show("Thank you.")
  End Sub
  Public Shared Sub Main()
    Dim form As MyForm = New MyForm()
    Application.Run(form)
  End Sub

End Class

In the InitializeComponent method, the form instantiates a RoundButton object and wires the RoundButton's Click event with the event handler roundButton_Click.

	roundButton = New RoundButton()
    AddHandler roundButton.Click, AddressOf roundButton_Click

Note that we did not define any event in the RoundButton class. Event-handling capability is inherited from the Control class.

The next thing is to set some of the properties of the RoundButton control.

 	roundButton.Text = "Click Here!"
    roundButton.BackgroundColor = System.Drawing.Color.White
    roundButton.Size = New System.Drawing.Size(80, 80)
    roundButton.Location = New System.Drawing.Point(100, 30)

And finally, add the control to the Controls collection of the form.

Me.Controls.Add(roundButton)

The Click event, when invoked by the user clicking the control, calls the roundButton_Click event handler, which simply displays a message box:

Private Sub roundButton_Click(ByVal source As Object, _
  ByVal e As EventArgs)
    MessageBox.Show("Thank you.")
End Sub

Conclusion

In this article, you have been introduced to the two important classes in the System.Windows.Forms namespace that you should understand when building a custom control: Control and UserControl. You have also learned to build your own custom control by directly extending the UserControl class and how to use your custom control in a Windows form.

Budi Kurniawan is a senior J2EE architect and author.


Return to the .NET DevCenter.