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
and 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 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 =
""
'get the area
code for the current zip
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
' get area
code
wsNodes =
xdoc.GetElementsByTagName("AREA_CODE")
For Each wsNode
In wsNodes
AreaCode =
wsNode.ChildNodes(0).Value
Next
' get city
name
wsNodes =
xdoc.GetElementsByTagName("CITY")
For Each wsNode
In wsNodes
City =
wsNode.ChildNodes(0).Value
Next
' get state
name
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 3: 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.v