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
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)
SetFromCurrency()
SetToCurrency()
AmountToExchange = Convert.ToDecimal(txtAmountToExchange.Text)
mXchange = New net.webservicex.www.CurrencyConvertor
Dim dblTemp As Double
dblTemp = mXchange.ConversionRate(FromCurrency, ToCurrency)
Result = dblTemp.ToString()
txtExchangeRate.Text = Result
txtExchangeRate.Enabled = True
ExchangedAmount = AmountToExchange * Result
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()
ddlFromCurrency = New DropDownList
ddlFromCurrency.ID = "ddlFromCurrency"
LoadDropDownList(ddlFromCurrency)
Me.Controls.Add(ddlFromCurrency)
ddlToCurrency = New DropDownList
ddlToCurrency.ID = "ddlToCurrency"
LoadDropDownList(ddlToCurrency)
Me.Controls.Add(ddlToCurrency)
txtExchangeRate = New TextBox
txtExchangeRate.ID = "txtExchangeRate"
txtExchangeRate.Enabled = False
txtExchangeRate.Text = Me.mCurrentProps.mResult
Me.Controls.Add(txtExchangeRate)
txtAmountToExchange = New TextBox
txtAmountToExchange.ID = "txtAmountToExchange"
txtAmountToExchange.Text = _
Me.mCurrentProps.mAmountToExchange
Me.Controls.Add(txtAmountToExchange)
txtExchangedAmount = New TextBox
txtExchangedAmount.ID = "txtExchangedAmount"
txtExchangedAmount.Enabled = False
txtExchangedAmount.Text = _
Me.mCurrentProps.mExchangedAmount
Me.Controls.Add(txtExchangedAmount)
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 FromCurrency
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)
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()
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()
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()
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()
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()
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.