Introduction
This article describes the construction of a simple custom control used to lookup an area code and a city/state location based upon a selected zip code; the lookup is performed through the use of an available public web service. The article includes the source code for this custom control as well as a demonstration site used to test the control.
The custom control contains a zip code property. Once set, the control will pass the zip code as an argument to a web method which will, if the zip code is valid, return the city, state, and area code associated with the zip code. The control will display the zip code, area code, and location whenever the zip code property is set or changed.
The demonstration web site included with this example provides an interface to change the zip code property for the control and to update the content displayed in the control. The web service is provided through www.webservicex.com, and is available for use at no cost.
Figure 1: Area Code Custom Control in Use
Getting Started
The files included with this project include a web control library project and a demonstration web site. In order to get started, open the included zip file and install the two projects onto your file system. Open IIS, and create a virtual directory for the web application. Open the solution into Visual Studio 2005 and make any changes necessary to bring both projects into the solution.
Once properly configured, your solution explorer should show these projects, references, and files:
Figure 2: Solution Explorer with Web App and Control Library
In examining the solution, note that the “AreaCodeLookup” control library contains only a single control and that control is called “AreaCodeLookup
”. This project also includes a web reference that points to the http://www.webservicex.net site; this public site supports the web service used to capture the area code and location information associated with the user supplied zip code.
The web application contains only a single web page (default.aspx) and includes a reference to the “AreaCodeLookup” DLL.
The web application serves as a container used to test the custom control; the default.aspx page contains an instance of the custom control as well as an interface for submitting zip codes to the control.
The Code: AreaCodeLookup
The “AreaCodeLookup” custom control is constructed to retrieve the information from the web service upon initialization and to use that information to populate a collection of properties; each of the properties is set to contain one of the values collected from the web service.
The web service returns the requested data in the form of an XML node; this node is imported into an XML document which is parsed to obtain the individual values used to populate the member variables. These member variables are in turn used to display the area code and location information when the page is rendered.
In examining the code, note that, aside from the default imports, only the System.XML
class has been added. The class itself inherits from the WebControl
class.
Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Xml
<DefaultProperty("ZipCode"), _
ToolboxData("<{0}:AreaCodeLookup runat=server>" & _
"</{0}:AreaCodeLookup>")> _
Public Class AreaCodeLookup
Inherits WebControl
Following the class declaration, a region entitled “Declarations” is created and within that region are the declarations for any of the private member variables used within the control. In this case, there is only a single member variable as I opted to store all of the property values directly into view state.
#Region "Declarations"
Private mGetAreaCode As net.webservicex.www.USZip
#End Region
After the variable declarations, there is another region defined (Methods) and within that region is the code used to capture the data from the web service, and to read the XML returned by that service and use the values to populate the properties. The initialization handler calls a subroutine called “GetAreaCode
” each time the control is initialized. GetAreaCode
accepts a single argument in the form of a string bearing the zip code.
Inside the GetAreaCode
subroutine, a new XML node is created. This node is populated with the return value derived from calling the web services “GetInfoByZip” web method. This node is checked to determine whether or not it is empty; if it is empty, the properties are set to contain an indication that the supplied zip code was invalid and the GetAreaCode
subroutine is exited. If the node is not empty, an XML document is created and the node is imported into that document. This XML document is then parsed and its values are captured into the object’s properties. When the control is rendered the properties are displayed to the user.
The code contained in the Methods region is as follows:
#Region "Methods"
Private Sub AreaCodeLookup_Init(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Init
AreaCode = ""
GetAreaCode(ZipCode)
End Sub
Public Sub GetAreaCode(ByVal zip As String)
Try
mGetAreaCode = New net.webservicex.www.USZip
Dim xn As XmlNode = mGetAreaCode.GetInfoByZIP(zip)
If xn.HasChildNodes = False Then
AreaCode = "XXX"
ZipCode = "INVALID"
City = "XXX"
State = "XX"
Exit Sub
End If
Dim xdoc As New XmlDocument
xdoc.AppendChild(xdoc.ImportNode(xn, True))
Dim wsNodes As XmlNodeList
Dim wsNode As XmlNode
wsNodes = xdoc.GetElementsByTagName("AREA_CODE")
For Each wsNode In wsNodes
AreaCode = wsNode.ChildNodes(0).Value
Next
wsNodes = xdoc.GetElementsByTagName("CITY")
For Each wsNode In wsNodes
City = wsNode.ChildNodes(0).Value
Next
wsNodes = xdoc.GetElementsByTagName("STATE")
For Each wsNode In wsNodes
State = wsNode.ChildNodes(0).Value
Next
Catch
AreaCode = "XXX"
ZipCode = "INVALID"
City = "XXX"
State = "XX"
End Try
End Sub
#End Region
The next region defined in the code is called “Properties”. Properties contains a collection of one public property and three private properties. The public property is used to contain the zip code; the other private properties contain the area code, city, and state. All property values are maintained in view state, these properties are:
#Region "Properties"
<Category("Zip Code")> _
<Browsable(True)> _
<Description("Enter the five number zip code.")> _
Public Property ZipCode() As String
Get
Dim s As String = CStr(ViewState("ZipCode"))
If s Is Nothing Then
Return String.Empty
Else
Return s
End If
End Get
Set(ByVal Value As String)
ViewState("ZipCode") = Value
End Set
End Property
Private Property AreaCode() As String
Get
Dim s As String = CStr(ViewState("AreaCode"))
If s Is Nothing Then
Return String.Empty
Else
Return s
End If
End Get
Set(ByVal Value As String)
ViewState("AreaCode") = Value
End Set
End Property
Private Property City() As String
Get
Dim s As String = CStr(ViewState("City"))
If s Is Nothing Then
Return String.Empty
Else
Return s
End If
End Get
Set(ByVal Value As String)
ViewState("City") = Value
End Set
End Property
Private Property State() As String
Get
Dim s As String = CStr(ViewState("State"))
If s Is Nothing Then
Return String.Empty
Else
Return s
End If
End Get
Set(ByVal Value As String)
ViewState("State") = Value
End Set
End Property
#End Region
The attributes of category, browsable, and description are used to provide design time support for the custom control. The category and description text will be displayed in the IDE’s property editor whenever this control is selected by the developer using the control.
Having captured the values from the XML string returned from the web service, and having populated the properties using the values contained in the XML, the only remaining step is to render the control. A region entitled, “Rendering” follows and it contains the code necessary to render the control on the page.
The code used to render the control is pretty simple; the HtmlTextWriter
is used to define a table and set up its characteristics (cell padding in this example), each row of the table contains two cells, a label and its associated value are placed into each of those two cells. The City
and State
properties are combined into a Location
string and displayed adjacent to the label reading “Location”. Once all of the data has been written into the table, the ending tag is rendered and the control is complete.
Naturally, you can change the configuration of the table or remove some of the data returned from the web service by making changes in the definition of the HTML as defined through the HtmlTextWriter
. If accessibility is an issue, you may wish to render the control based upon the user of a “div” in lieu of the table. The rendering operation is wrapped up in a Try-Catch
block; if the rendering operation fails, the catch block will display some placeholder text. The RenderContents
subroutine is overridden and the HTML is formatted within this subroutine through the use of the HtmlTextWriter
.
#Region "Rendering"
Protected Overrides Sub RenderContents(ByVal _
output As HtmlTextWriter)
Try
output.AddAttribute(HtmlTextWriterAttribute.Cellpadding, "3")
output.RenderBeginTag(HtmlTextWriterTag.Table)
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.AddAttribute(HtmlTextWriterAttribute.Align, "left")
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.Write("<b>Zip Code: </b>")
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.Write(ZipCode)
output.RenderEndTag()
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.AddAttribute(HtmlTextWriterAttribute.Align, "left")
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.Write("<b>Area Code: </b>")
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.Write(AreaCode)
output.RenderEndTag()
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.AddAttribute(HtmlTextWriterAttribute.Align, "left")
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.Write("<b>Location: </b>")
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.Write(City & ", " & State)
output.RenderEndTag()
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.RenderEndTag()
output.RenderEndTag()
Catch ex As Exception
output.Write("Area Code From Zip")
End Try
End Sub
#End Region
The Code: The Demo Site’s Default Page
The default.aspx page contained within the demo site serves only a test container for the control. The page contains a Label
control used to title the page, a textbox used to capture zip codes, and a button to submit requests. The property editor may be used in the IDE to set the zip code property or, as is indicated in the submit button event handler, the zip code property may be edited while the web application is in use.
Figure 3: Setting the Zip Code Property at Design Time
The button click event handler’s code is as follows:
Protected Sub btnLookup_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnLookup.Click
If Not String.IsNullOrEmpty(txtZip.Text.ToString()) Then
Try
Dim chr() As Char = txtZip.Text.ToCharArray()
Dim iLoop As Integer
For iLoop = 0 To chr.Length - 1
If Char.IsLetter(chr(iLoop)) Then
txtZip.Text = "INVALID"
Exit Sub
End If
Next
AreaCodeLookup1.ZipCode = txtZip.Text
AreaCodeLookup1.GetAreaCode(txtZip.Text)
Catch ex As Exception
End Try
End If
End Sub
Figure 4: After Entering an Invalid Zip Code
The handler is pretty straight forward, the contents of the text box used to capture the zip code is first checked to make sure it is not empty. If isn’t empty, the contents are checked to make sure that the value does not contain characters. If characters are encountered the text box text is set to display INVALID and the event handler subroutine is exited. If the text box contains a valid entry, the custom control’s zip code property is set and then the control’s GetAreaCode
subroutine is called with text box text value passed in as the zip code argument.
From there, the custom control will, through the web service, update the area code, city, and state values which will in turn be displayed on the page through the control.
Summary
This project was intended to describe a useful, easy to build custom control. While this demonstration was limited to describing the AreaCodeLookup
custom control, the same approach applied herein would work with a variety of other custom controls. The project itself describes some of the procedures that may be used to communicate with a web service through a custom control.