Convert Currency Values with a Custom Control in VB.NET

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.

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).

MoneyChanger1-in-VB.NET.gif

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:

MoneyChanger2-in-VB.NET.gif

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.