Introduction:
This article describes the details for constructing a
custom ASP.NET 2.0 composite control used to convert one form of currency into
another. The control consumes a public web service in order to calculate the
exchange rate and uses the exchange rate returned from the web service to
calculate the value of the exchanged currency.
The operation of the control is simple enough, the user
keys in the amount of money to be exchanged, then, using drop down lists, sets
the currency type to exchange from and exchange to; once these three values are
set, the user may click on the submit button at the bottom of the control. On
submit the control will evoke the web service, passing it the two currency
formats; the web service then returns the exchange rate. The control then uses
the exchange rate to calculate the value of the currency once converted to the
new currency type. This information is then all displayed to the end user.
As a composite control, it may be dropped onto to any
web page and used to provide that functionality to the page with no additional
coding required.
The demonstration control library contains only the
single control; the control itself maintains state through the use of ASP.NET
2.0 control state. The rendering of the control was kept simple. The attached
files also include a sample website that is merely used as a container for the
demonstration control.
An example of the control in use on a web page is
provided in the following figure (figure 1).
Figure 1: Currency Conversion 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 Visible
In examining the solution, note that the
"CurrencyConvertControl" control library contains only a single control and that
control is called "MoneyChanger". This project also includes a web reference
that points to the
http://www.webservicex.net
site; this public site provides and supports the free web service used to
calculate the exchange rates between many different types of currency.
The web application contains only a single web page
(default.aspx) and includes a reference to the "CurrencyConvertControl" dynamic
link library.
The web application serves as a container used to test
the custom control.
The Code: MoneyChanger
The "MoneyChanger" custom composite control is
constructed to retrieve the information from the web service upon initialization
and to use that information to display the current exchange rate and to
calculate the conversion of money following conversion from one currency type to
the next. The control supports about 150 different currency types.
The web service accepts to arguments, the currency type
to exchange from and the currency type to exchange to; in response to a request,
the service will return a double that contains the exchange rate between the two
currencies and that calculate is sensitive to the direction the currency is
being converted (e.g., Euros to US dollars will not yield the same result as US
dollars to Euros).
In examining the code, note that only the default
imports are included in the class. The class itself inherits from the
CompositeControl class.
Imports
System.Collections.Generic
Imports
System.ComponentModel
Imports
System.Text
Imports
System.Web
Imports
System.Web.UI
Imports
System.Web.UI.WebControls
<ToolboxData("<{0}:MoneyChanger
runat=server></{0}:MoneyChanger>")> _
Public
Class MoneyChanger
Inherits CompositeControl
Following the class declaration, a region entitled
"Declarations" is created and within that region are the declarations for all of
the objects used within the control. The serializable structure is used to
maintain control state; the structure contains the member variables used by the
control. Aside from that, an event is declared for use in conjunction with the
submit button, an instance of the serializable structure (CurrentProperties) is
created, each of the control used is declared, and the web service is declared.
#Region
"Declarations"
Public Event Change(ByVal
Sender As Object,
ByVal E As
EventArgs)
Private mCurrentProps As
New CurrentProperties
Public ddlFromCurrency As DropDownList
Public ddlToCurrency As DropDownList
Public txtExchangeRate As TextBox
Public txtAmountToExchange As TextBox
Public txtExchangedAmount As TextBox
Public btnSubmit As Button
Private mXchange As
net.webservicex.www.CurrencyConvertor
<Serializable()> _
Private Structure CurrentProperties
Dim mFromCurr As
Integer
Dim mToCurr As
Integer
Dim mResult As
Double
Dim mAmountToExchange As
Decimal
Dim mExchangedAmount As
Decimal
End Structure
#End
Region
After the variable declarations, there is another
region defined (Methods) and within that region is the code used to drive the
control.
The code contained in the Methods region is as follows:
#Region
"Methods"
Private Sub MoneyChanger_Init(ByVal
sender As Object,
ByVal e As
System.EventArgs)
Handles Me.Init
' required to
enable control state
Page.RegisterRequiresControlState(Me)
End Sub
The first subroutine used in the control is the
initialization event handler; this handler is used to set the Page property used
to allow the use of ASP.NET 2.0 control state. The next subroutine contained in
the methods region is the button click event handler used to handle the submit
button click. This subroutine connects to the web service and requests the
exchange rate based upon the users supplied currency selections. The handler
also performs the calculation needed to convert the currency amount based upon
the exchange rate between the two currency types. Once the exchange rate is
received and conversion calculated, the handler updates the information
displayed to the user.
Private
Sub btnSubmit_Click(ByVal
Sender As Object,
ByVal E As
EventArgs)
'update the
to and from currency values from the drop down lists
SetFromCurrency()
SetToCurrency()
'capture the
amount to be exchanged into a property
AmountToExchange =
Convert.ToDecimal(txtAmountToExchange.Text)
' instance
and call the web service to get the conversion rate
mXchange =
New net.webservicex.www.CurrencyConvertor
Dim dblTemp As
Double
dblTemp =
mXchange.ConversionRate(FromCurrency, ToCurrency)
Result = dblTemp.ToString()
'update the
exchange rate textbox to show the returned change rate
txtExchangeRate.Text =
Result
txtExchangeRate.Enabled =
True
'calculate
the value of the money once converted to the new currency
'type and
then store it into a property
ExchangedAmount =
AmountToExchange * Result
'convert the
ExchangedAmount variable to the proper format
'and display
it in the control
txtExchangedAmount.Text =
ExchangedAmount.ToString("##.00")
txtExchangedAmount.Enabled
= True
OnChange(EventArgs.Empty)
End
Sub
The
next three subroutines are short and simple:
Protected
Sub OnChange(ByVal
E As EventArgs)
RaiseEvent Change(Me, E)
End
Sub
Protected
Overrides Function
SaveControlState() As
Object
Return Me.mCurrentProps
End
Function
Protected
Overrides Sub
LoadControlState(ByVal savedState
As Object)
mCurrentProps =
New CurrentProperties
mCurrentProps =
CType(savedState, CurrentProperties)
End
Sub
OnChange is used to raise an event; this is part of the
code related to processing the submit button click event. The other two
subroutines are used to save and load control state between postbacks.
Next up is the overridden version of the
CreateChildControls subroutine; in this subroutine, all of the controls are
instanced, populated, and added to the control. The comments describe each
section of the subroutine.
Protected
Overrides Sub
CreateChildControls()
' create the
dropdown list used to display
' the from
currency types
ddlFromCurrency =
New DropDownList
ddlFromCurrency.ID =
"ddlFromCurrency"
LoadDropDownList(ddlFromCurrency) 'populates DDL
with currency types
Me.Controls.Add(ddlFromCurrency)
' create the
dropdown list used to display
' the to
currency types
ddlToCurrency =
New DropDownList
ddlToCurrency.ID =
"ddlToCurrency"
LoadDropDownList(ddlToCurrency) 'populates DDL with
currency types
Me.Controls.Add(ddlToCurrency)
' create the
textbox used to display
' the
exchange rate
txtExchangeRate =
New TextBox
txtExchangeRate.ID =
"txtExchangeRate"
txtExchangeRate.Enabled =
False
txtExchangeRate.Text =
Me.mCurrentProps.mResult
'display result value if any
Me.Controls.Add(txtExchangeRate)
' create the
textbox used to capture and display
' the number
of units of one type of currency
' to convert
into another type of currency.
' Defaults to
enabled = false since it will
' not contain
any data and the user won't manually
' enter this
calculated value
txtAmountToExchange =
New TextBox
txtAmountToExchange.ID =
"txtAmountToExchange"
txtAmountToExchange.Text =
Me.mCurrentProps.mAmountToExchange
Me.Controls.Add(txtAmountToExchange)
' create the
textbox used to display the exchange
' rate
calculated and returned by the web service
txtExchangedAmount =
New TextBox
txtExchangedAmount.ID =
"txtExchangedAmount"
txtExchangedAmount.Enabled
= False
txtExchangedAmount.Text =
Me.mCurrentProps.mExchangedAmount
Me.Controls.Add(txtExchangedAmount)
' creats the
submit button and assigns it a handler
btnSubmit =
New Button
btnSubmit.Text =
"Submit"
AddHandler btnSubmit.Click, AddressOf
btnSubmit_Click
Me.Controls.Add(btnSubmit)
End
Sub
Next up
is the subroutine used to set the ToCurrency and FromCurrency properties used to
the user selected values shown in each drop down list. These are properties used
internally to the control and represent the to and from currency arguments
passed to the web service. Due to some issues with the enumeration used by the
web service to represent the currency types, it was necessary to populate each
drop down list item with a value representing the enumeration value as an
integer. The To and From Currency properties are actually set to read the value
rather than the index or text contained in each drop down list.
Private
Sub SetFromCurrency()
FromCurrency =
ddlFromCurrency.SelectedValue
End
Sub
Private
Sub SetToCurrency()
ToCurrency =
ddlToCurrency.SelectedValue
End
Sub
The
next subroutine is used to populate the drop down lists used to display the to
and from currency types; there are about 150 different types so I will not show
the whole subroutine since it is largely repetitive. The subroutine accepts the
drop down list control as an argument and clears and populates the drop down
list whenever this subroutine is called.
Private
Sub
LoadDropDownList(ByVal
ddl As
DropDownList)
ddl.Items.Clear()
ddl.Items.Add("AED-UAE Dirham")
ddl.Items(0).Value = 139
ddl.Items.Add("AFA-Afghanistan Afghani")
ddl.Items(1).Value = 0
ddl.Items.Add("ALL-Albanian Lek")
ddl.Items(2).Value = 1...
The
next region defined in the code is called "Properties"; this section contains
the properties used by the control. In this case, all of the properties are
declared as private since all are only used internally by the control; this
prevents the properties from being displayed in the IDE at design time. If you
wanted these to appear in the property editor, you would need to convert them to
public properties and apply the appropriate attributes (category, browsable,
descriptions, etc.) to provide adequate design time support. Note that the call
to EnsureChildControls and to SaveControlState are included in each property.
Both calls are made to keep the controls update to date within the control and
to maintain control state in keeping with changes made to the properties.
#Region
"Properties"
Private Property Result()
As Double
Get
EnsureChildControls()
Return mCurrentProps.mResult
End Get
Set(ByVal value
As Double)
EnsureChildControls()
mCurrentProps.mResult =
value
SaveControlState()
End Set
End Property
Private Property FromCurrency()
As Integer
Get
EnsureChildControls()
Return mCurrentProps.mFromCurr
End Get
Set(ByVal value
As Integer)
EnsureChildControls()
mCurrentProps.mFromCurr =
value
SaveControlState()
End Set
End Property
Private Property ToCurrency()
As Integer
Get
EnsureChildControls()
Return mCurrentProps.mToCurr
End Get
Set(ByVal value
As Integer)
EnsureChildControls()
mCurrentProps.mToCurr =
value
SaveControlState()
End Set
End Property
Private Property AmountToExchange()
As Decimal
Get
EnsureChildControls()
Return mCurrentProps.mAmountToExchange
End Get
Set(ByVal value
As Decimal)
EnsureChildControls()
mCurrentProps.mAmountToExchange = value
SaveControlState()
End Set
End Property
Private Property ExchangedAmount()
As Decimal
Get
EnsureChildControls()
Return mCurrentProps.mExchangedAmount
End Get
Set(ByVal value
As Decimal)
EnsureChildControls()
mCurrentProps.mExchangedAmount = value
SaveControlState()
End Set
End Property
#End
Region
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 control are placed into each of those two cells. 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. 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)
' Amount to
Exchange
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddAttribute(HtmlTextWriterAttribute.For, txtAmountToExchange.ClientID)
output.RenderBeginTag(HtmlTextWriterTag.Label)
output.Write("Amount
to Exchange: ")
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddStyleAttribute(HtmlTextWriterStyle.FontFamily,
Me.Font.Name)
output.AddAttribute(HtmlTextWriterAttribute.Size, Me.Font.Size.ToString())
txtAmountToExchange.RenderControl(output)
output.RenderEndTag()
output.RenderEndTag()
output.RenderEndTag()
' from
currency
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddAttribute(HtmlTextWriterAttribute.For, Me.ddlFromCurrency.ClientID)
output.RenderBeginTag(HtmlTextWriterTag.Label)
output.Write("Convert
from this currency: ")
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddStyleAttribute(HtmlTextWriterStyle.FontFamily,
Me.Font.Name)
output.AddAttribute(HtmlTextWriterAttribute.Size, Me.Font.Size.ToString())
ddlFromCurrency.RenderControl(output)
output.RenderEndTag()
output.RenderEndTag()
output.RenderEndTag()
' to currency
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddAttribute(HtmlTextWriterAttribute.For, Me.ddlToCurrency.ClientID)
output.RenderBeginTag(HtmlTextWriterTag.Label)
output.Write("Convert
to this currency: ")
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddStyleAttribute(HtmlTextWriterStyle.FontFamily,
Me.Font.Name)
output.AddAttribute(HtmlTextWriterAttribute.Size, Me.Font.Size.ToString())
ddlToCurrency.RenderControl(output)
output.RenderEndTag()
output.RenderEndTag()
output.RenderEndTag()
' exchange
rate
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddAttribute(HtmlTextWriterAttribute.For, Me.txtExchangeRate.ClientID)
output.RenderBeginTag(HtmlTextWriterTag.Label)
output.Write("Exchange
Rate: ")
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Td)
txtExchangeRate.Text =
mCurrentProps.mResult
output.AddStyleAttribute(HtmlTextWriterStyle.FontFamily,
Me.Font.Name)
output.AddAttribute(HtmlTextWriterAttribute.Size, Me.Font.Size.ToString())
txtExchangeRate.RenderControl(output)
output.RenderEndTag()
output.RenderEndTag()
output.RenderEndTag()
' Exchanged
Amount
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddAttribute(HtmlTextWriterAttribute.For, txtExchangedAmount.ClientID)
output.RenderBeginTag(HtmlTextWriterTag.Label)
output.Write("Amount
After Exchange: ")
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddStyleAttribute(HtmlTextWriterStyle.FontFamily,
Me.Font.Name)
output.AddAttribute(HtmlTextWriterAttribute.Size, Me.Font.Size.ToString())
txtExchangedAmount.RenderControl(output)
output.RenderEndTag()
output.RenderEndTag()
output.RenderEndTag()
' submit
button
output.RenderBeginTag(HtmlTextWriterTag.Tr)
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddAttribute(HtmlTextWriterAttribute.For, Me.btnSubmit.ClientID)
output.AddStyleAttribute(HtmlTextWriterStyle.FontFamily,
Me.Font.Name)
output.AddAttribute(HtmlTextWriterAttribute.Size, Me.Font.Size.ToString())
output.RenderBeginTag(HtmlTextWriterTag.Label)
output.Write("Retrieve
Exchange Rate: ")
output.RenderEndTag()
output.RenderBeginTag(HtmlTextWriterTag.Td)
output.AddStyleAttribute(HtmlTextWriterStyle.FontFamily,
Me.Font.Name)
output.AddAttribute(HtmlTextWriterAttribute.Size, Me.Font.Size.ToString())
btnSubmit.RenderControl(output)
output.RenderEndTag()
output.RenderEndTag()
output.RenderEndTag()
output.RenderEndTag()
Catch
output.Write("Money
Changer Custom Control")
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 only a line of
text used to describe the page and the control itself. Displaying the
default.aspx page in a browser will enable testing of the control.
Summary
This project was intended to describe a useful, easy to
build custom composite control. While this demonstration was limited to
describing the Money Changer custom composite control, the same approach applied
herein would work with a variety of other custom composite controls. It does
provide an example of consuming a web service within a custom control and that
approach could be applied to displaying information from any web service. The
web service consumed in this demonstration is not under my control and I can
predict the longevity of the service.