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
TextBox, to controls that are richer and more complex, such as
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
UserControl classes. Incorporating existing controls saves you the trouble of providing your own user interface. Extending the
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.
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
UserControl classes. They are followed by the section that explains how to build your own custom control: "The RoundButton Control."
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 following are some of the
Control class' most important properties:
System.Drawing.Colorobject. You can programmatically assign a
System.Drawing.Colorobject to this property using code like the following:
control.BackColor = System.Drawing.Color.Red
Button1was added to "Thank you.":
Button1.Parent.Text = "Thank you."
Stringthat is associated with the control. For example, in a
Textproperty is the
Stringthat appears on the label body.
Some of the
Control class' frequently used methods are:
System.Drawing.Graphicsobject of the control on which you can draw using the various methods of the
System.Drawing.Graphicsclass. For instance, the following code obtains the
Graphicsobject 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.
False, so that it is not shown.
TextChanged. For example, calling the
OnClickmethod of the control will trigger its
Trueso that the control is shown.
UserControlclass provides an empty control that can be used to create other controls. It is an indirect child of the
Controlclass. 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
UserControl class inherits all of the standard positioning and mnemonic-handling code from the
ContainerControl class. This code is needed in a user control.
Having the two classes,
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
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
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
Dim brush As SolidBrush = New SolidBrush(backgroundColor)
Now you can start drawing. For the base, you use the
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.DrawEllipse(pen, Cint(penWidth/2), _ CInt(penWidth/2), Width - penWidth, Height - penWidth)
Finally, you draw the text on the Graphics object using the
graphics.DrawString(Text, font, textBrush, penWidth, _ Height / 2 - fontHeight)
RoundButton control is shown in Figure 1.
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
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
InitializeComponent method, the form instantiates a
RoundButton object and wires the
Click event with the event handler
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
The next thing is to set some of the properties of the
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.
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
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:
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.
Copyright © 2009 O'Reilly Media, Inc.