AddThis Social Bookmark Button

Print

Programming ASP.NET: Custom and User Controls, Part 2
Pages: 1, 2, 3, 4, 5

Modifying the CountedButton derived control

CountedButton needs only minor modification, as shown in Example 14-18 for C# and Example 14-19 for VB.NET.

Example 14-18: The modified CountedButton.cs file


using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
 
namespace CustomControls
{
   // custom control derives from button
   public class CountedButton : System.Web.UI.WebControls.Button
   {
 
      private string displayString;
 
      // default constructor
      public CountedButton(  )
      {
         displayString = "clicks";
         InitValues(  );
      }
 
      // overloaded, takes string to display (e.g., 5 books)
      public CountedButton(string displayString)
      {
         this.displayString = displayString;
         InitValues(  );
      }
 
      // called by constructors
      private void InitValues(  )
      {
         if (ViewState["Count"] == null)
            ViewState["Count"] = 0;
         this.Text = "Click me";
      }
   
      // count as property maintained in view state
      public int Count 
      {
         get
         {
            // initialized in constructor
            // can not be null
            return (int) ViewState["Count"];
         }
 
         set
         {
            ViewState["Count"] = value;
         }
      }
 
      // override the OnClick to increment the count,
      // update the button text and then invoke the base method
      protected override void OnClick(EventArgs e)
      {
         ViewState["Count"] =  ((int)ViewState["Count"]) + 1;
         this.Text = ViewState["Count"] + " " + displayString;
         base.OnClick(e);
      }
   }
}

Example 14-19: The modified CountedButton.vb file


Imports System.ComponentModel
Imports System.Web.UI
Imports System.Web.UI.WebControls
 
' custom control derives from button
Public Class CountedButton
   Inherits System.Web.UI.WebControls.Button
 
   Private displayString As String
 
   ' constructor initializes view state value
   Public Sub New(  )
      displayString = "clicks"
      Init(  )
   End Sub
 
  ' overloaded, takes string to display (e.g., 5 books)
   Public Sub New(ByVal displayString As String)
      Me.displayString = displayString
      Init(  )
   End Sub
 
   ' called by constructors
   Private Shadows Sub Init(  )
      If ViewState("Count") = Is Nothing Then
         ViewState("Count") = 0
         Me.Text = "Click me"
      End If
   End Sub
 
   ' count as property maintained in view state
   Public Property Count(  ) As Integer
      Get
         Return CInt(ViewState("Count"))
      End Get
      Set(ByVal Value As Integer)
         ViewState("Count") = Value
      End Set
   End Property
 
   ' override the OnClick to increment the count,
   ' update the button text and then invoke the base method
   Protected Overrides Sub OnClick(ByVal e As EventArgs)
      ViewState("Count") = CInt(ViewState("Count")) + 1
      Me.Text = CStr(ViewState("Count") & " " & displayString
      MyBase.OnClick(e)
   End Sub
End Class

Because you want the button to be able to display the string 5 Inquiries rather than 5 clicks, you must change the line within the OnClick method that sets the button's text:

this.Text = ViewState["Count"] + " " + displayString;

The VB.NET equivalent is:

Me.Text = ViewState("Count") & " " & displayString

Rather than hard-wiring the string, you'll use a private member variable, displayString, to store a value passed in to the constructor:

private string displayString;

In VB.NET, you'd use:

Private displayString As String

You must set this string in the constructor. To protect client code that already uses the default constructor (with no parameters), you'll overload the constructor, adding a version that takes a string:

public CountedButton(string displayString)
 {
    this.displayString = displayString;
    Init(  );
 }

In VB.NET, the code is:

Public Sub New(ByVal displayString As String)
   Me.displayString = displayString
   Initialize(  )
End Sub

You can now modify the default constructor to set the displayString member variable to a reasonable default value. In C#, the code is:

public CountedButton(  )
{
   displayString = "clicks";
   InitValues(  );
}

In VB.NET, use:

Public Sub New(  )
   displayString = "clicks"
   Init(  )
End Sub

The code common to both constructors has been factored out to the private helper method Init, which ensures that the Count property is initialized to zero and sets the initial text for the button:

private void Init(  )
{
   if (ViewState["Count"] == null)
      ViewState["Count"] = 0;
   this.Text = "Click me";
}

In VB.NET, the same thing is accomplished using:

Private Shadows Sub Init(  )
   If ViewState("Count") = Nothing Then
      ViewState("Count") = 0
      Me.Text = "Click me"
   End If
End Sub

With these changes, the CountedButton is ready to be used in the first composite control, BookCounter.

Creating the BookCounter composite control

The BookCounter composite control is responsible for keeping track of and displaying the number of inquiries about an individual book. Its complete source code is shown in C# in Example 14-20 and in VB.NET in Example 14-21.

Example 14-20: BookCounter.cs


using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
 
namespace CustomControls
{
   public class BookCounter : 
      System.Web.UI.WebControls.WebControl, 
      INamingContainer
   {
   
      // intialize the counted button member
      CountedButton btn = new CountedButton("inquiries");
    
      public string BookName 
      {
         get
         {
            return (string) ViewState["BookName"];
         }
 
         set
         {
            ViewState["BookName"] = value;
         }
      }
 
      public int Count
      {
         get
         {
            return btn.Count;
         }
         set
         {
            btn.Count = value;
         }
      }
 
      public void Reset(  )
      {
         btn.Count = 0;
      }
 
      protected override void CreateChildControls(  )
      {
         Controls.Add(btn);
      }
   }
}

Example 14-21: BookCounter.vb


Imports System
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.ComponentModel
 
Public Class BookCounter
   Inherits System.Web.UI.WebControls.WebControl
   Implements INamingContainer
 
   ' intialize the counted button member
   Public btn As CountedButton = New CountedButton("inquiries")
 
   Public Property BookName(  ) As String
      Get
         Return CStr(ViewState("BookName"))
      End Get
      Set(ByVal Value As String)
         ViewState("BookName") = Value
      End Set
   End Property
 
   Public Property Count(  ) As Integer
      Get
         Return btn.Count
      End Get
      Set(ByVal Value As Integer)
         btn.Count = Value
      End Set
   End Property
 
   Public Sub Reset(  )
      btn.Count = 0
   End Sub
 
   Protected Overrides Sub CreateChildControls(  )
      Controls.Add(btn)
   End Sub
 
End Class

INamingContainer

The first thing to note about the BookCounter class is that it implements the INamingContainer interface. This is a "marker" interface that has no methods. The purpose of this interface is to identify a container control that creates a new ID namespace, guaranteeing that all child controls have IDs that are unique to the application.

Containing CountedButton

The BookCounter class contains an instance of CountedButton:

CountedButton btn = new CountedButton("inquiries");

or:

Public btn As CountedButton = New CountedButton("inquiries")

The btn member is instantiated in the CreateChildControls method inherited from System.Control:

protected override void CreateChildControls(  )
{
   Controls.Add(btn);
}

The VB.NET equivalent is:

Protected Overrides Sub CreateChildControls(  )
   Controls.Add(btn)
End Sub

CreateChildControls is called in preparation for rendering and offers the BookCounter class the opportunity to add the btn object as a contained control.

There is no need for BookCounter to override the Render method; the only thing it must render is the CountedButton, which can render itself. The default behavior of Render is to render all the child controls, so you need not do anything special to make this work.

BookCounter also has two properties: BookName and Count. BookName is a string to be displayed in the control and is managed through ViewState. Its C# source code is:


public string BookName 
{
    get
    {
        return (string) ViewState["BookName"];
    }
 
    set
    {
        ViewState["BookName"] = value;
    }
}

Its VB.NET source code is:


Public Property BookName(  ) As String
   Get
      Return CStr(ViewState("BookName"))
   End Get
   Set(ByVal Value As String)
      ViewState("BookName") = Value
   End Set
End Property

Count is the count of inquires about this particular book; responsibility for keeping track of this value is delegated to the CountedButton. In C#, the code is:


public int Count
{
   get
   {
      return btn.Count;
   }
   set
   {
      btn.Count = value;
   }
}

and in VB.NET, it's:


Public Property Count(  ) As Integer
   Get
      Return btn.Count
   End Get
   Set(ByVal Value As Integer)
      btn.Count = Value
   End Set
End Property

There is no need to place the value in ViewState, since the button itself is responsible for its own data.

Pages: 1, 2, 3, 4, 5

Next Pagearrow