Specializing an Existing Control

You'll create a customized button class that keeps track of how often it has been clicked. To begin, open a new project in Visual Studio .NET. In the Project Type window, choose your language of choice, and in the Templates window, choose Windows Control Library. Be sure to set an appropriate location, and name the library CountedControl.

This example is similar to a custom control demonstrated in Programming ASP.NET, but there are significant differences in how custom controls are created for use with Windows Forms.

You are placed in the designer for a user control. That isn't quite what you want (you'll be creating a user control in the next example), but it isn't a problem. Right-click on the form and choose View Code. Modify the class name to CountedButton and the base class from UserControl to Button:

public class CountedButton : System.Windows.Forms.Button

Public Class CountedButton Inherits System.Windows.Forms.Button

In C#, you must also modify the constructor so it has the same name as the class:

public CountedButton( )

While you are at it, change the filename from UserControl1 to CountedButton.cs or CountedButton.vb.

When you change the base type from UserControl to Button, Visual Studio .NET will no longer use the Forms Designer in Design view. You'll add to the new derived button type entirely through code.

Override the OnPaint method to draw the number of times the button was clicked:

protected override void OnPaint (PaintEventArgs e) { if ( numClicks = = 0 ) this.Text = "Never been clicked"; else this.Text = "Clicked " + numClicks + " times"; base.OnPaint(e); }

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs) If numClicks = 0 Then Me.Text = "Never been clicked" Else Me.Text = "Clicked " & numClicks & " times" End If MyBase.OnPaint(e) End Sub

Finally, override the on-click method to update the number of clicks, and invalidate the button so it will be repainted.

protected override void OnClick( EventArgs e ) { numClicks++; Invalidate( ); base.OnClick(e); }

Protected Overrides Sub OnClick(ByVal e As EventArgs) numClicks = numClicks + 1 Invalidate( ) MyBase.OnClick(e) End Sub

That's all there is to it! Build your control, and you are ready to test it. The complete source code for the custom control in C# is shown in Example 17-1 and the VB.NET version is shown in Example 17-2.

Example 17-1. Custom derived control (C#)

using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Windows.Forms; namespace CountedButtonCS1 { // change the base class to button public class CountedButton : System.Windows.Forms.Button { private System.ComponentModel.Container components = null; private int numClicks = 0; // keep track of number of clicks public CountedButton( ) { InitializeComponent( ); } // override OnPaint to display the number of clicks protected override void OnPaint (PaintEventArgs e) { if ( numClicks = = 0 ) this.Text = "Never been clicked"; else this.Text = "Clicked " + numClicks + " times"; base.OnPaint(e); } // when the button is clicked update the counter // and invalidate the control protected override void OnClick( EventArgs e ) { numClicks++; Invalidate( ); base.OnClick(e); } ///

/// Clean up any resources being used. ///

protected override void Dispose( bool disposing ) { if( disposing ) { if( components != null ) components.Dispose( ); } base.Dispose( disposing ); } #region Component Designer generated code #endregion } }

Example 17-2. Custom derived control (VB.NET)

' change the base class to button Public Class CountedButton Inherits System.Windows.Forms.Button ' keep track of number of clicks Private numClicks As Integer = 0 Public Sub New( ) MyBase.New( ) InitializeComponent( ) End Sub ' override OnPaint to display the number of clicks Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs) If numClicks = 0 Then Me.Text = "Never been clicked" Else Me.Text = "Clicked " & numClicks & " times" End If MyBase.OnPaint(e) End Sub ' when the button is clicked update the counter ' and invalidate the control Protected Overrides Sub OnClick(ByVal e As EventArgs) numClicks = numClicks + 1 Invalidate( ) MyBase.OnClick(e) End Sub #Region " Windows Form Designer generated code " #End Region End Class

17.1.1 Testing the Control

Of course, you can't just "run" a control; you need a container, such as a form, within which to test the control. Create a new Windows project in your language of choice and add a reference to the control project. To do so from within Visual Studio .NET, right-click on References and choose Add Reference. Click on the Projects tab, and then click the Browse button. Navigate to the dll you created for your control, and double-click on it. Then click OK to close the Add Reference dialog.

You can add your control to your toolbox by right-clicking on the toolbox and choosing Customize Toolbox. Choose the .NET Framework Components tab, and then click the Browse button. Navigate to and double-click on your dll; the control appears at the bottom of your toolbox.

You can create your control in one language (e.g., C# or VB.NET) and your test program in another.

Double-click on the control to add it to the form, and size it to display the full message. Run the test program and click on the button. The result, shown in Figure 17-1, is that the button keeps track of how often it is clicked.

Figure 17-1. The CountedButton control

17.1.2 Adding Properties

One powerful use for custom controls is to expose custom properties. You might derive from the Button class only to expose new and interesting properties to your clients. In the next example, you'll modify your CountedButton class to add a property that reveals how often the button has been clicked.

The change to the control is almost trivial. Just add a property to the control to provide access to the number of times the button was clicked:

public int NumClicks { get { return numClicks; } set { numClicks = value; } }

Public Property numClicks( ) As Integer Get Return _numClicks End Get Set(ByVal Value As Integer) _numClicks = Value End Set End Property

Because I wanted the property to be named numClicks, I had to change the private field to _numClicks (note the underscore). VB.NET is case insensitive, so you can't use the idiom common in C# of naming the field with a lowercase letter and the property with an uppercase letter.

You can use this property in your test program. Add a label (named lblOut) and a button (named btnUpdate) to your form. Add an event handler for the button to update the label based on the numClicks property:

private void btnUpdate_Click(object sender, System.EventArgs e) { lblOut.Text = "The button was clicked " + countedButton1.NumClicks.ToString( ) + " times."; }

Private Sub btnUpdate_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnUpdate.Click lblOut.Text = _ "The button was clicked " & _ CountedButton1.numClicks.ToString( ) & _ " times." End Sub

The key line of code is shown in bold. Determine how many times the button was pressed by accessing its numClicks property.

Категории