Introduction
In the last article, Currency Conversion WCF Service For Silverlight, I discussed how you will go about writing a WCF service that can be consumed by Silverlight clients or all clients that can consume ASMX based web services. This article will discuss how to write a Silverlight application that will consume WCF service. The following image shows how the UI for the application looks like:
As you can see, it has a very simple layout. There are 2 dropdown combo boxes that contain a list of currencies available, and a button that you can click to submit selected currencies to get the conversion rate.
Adding Reference to WCF Service
Since the whole application depends on communication with a WCF service that serves currency conversion rates, the first thing that needs to be done is to add a reference to the service so the proxy classes can be created. The application uses these proxy classes to connect to WCF service. You can right click on References or Service Reference node in solution explorer and then follow the wizard steps to add a reference to WCF service that we created. The following images show the process of adding a service reference.
Populating Silverlight ComboBox
There are two input data points for this Silverlight control or application. A source (from) and target (to) currency for which you want to get the conversion rate. The application itself does not contain that list. This list comes from the WCF service that I implemented. In this day and age where bigger countries get broken into smaller ones, it made more sense to serve this list from service instead of having a hard coded list in the application itself. Let's look at the XAML markup for ComboBox
.
<ComboBox Grid.Column="1" Grid.Row="1" x:Name="cbFromCurrency"
ItemsSource="{Binding}" Width="250"
IsEnabled="False" Margin="0 0 0 10">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Country}"></TextBlock>
<TextBlock Text=","></TextBlock>
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBlock Text="("></TextBlock>
<TextBlock Text="{Binding Symbol}"></TextBlock>
<TextBlock Text=")"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
For demo purposes, I am rendering ComboBox
items using DataTemplate
. I could have just bound each item to one display field in data items. You can see from markup that combobox
is being bound to a data source using Binding
through ItemSource
property of the control. And in DataTemplte
each display element is also using Binding
to specific path or property in data item. For example first TextBlock
is bound to Country
property of Currency
data item. The codebehind that binds these 2 ComboBox
controls is as below:
void BindCurrencyDropDowns()
{
cbFromCurrency.DataContext = _currencyList;
cbFromCurrency.SelectedIndex = 0;
cbToCurrency.DataContext = _currencyList;
cbToCurrency.SelectedIndex = 1;
cbToCurrency.IsEnabled = cbFromCurrency.IsEnabled =
btnSubmit.IsEnabled = _currencyList.Count > 0;
}
When the control loads, it calls into WCF service to get the list of currencies. Our WCF service exposes a method GetCurrencies
that returns a list of Currency
objects representing currencies for which conversion rates are available. The following code snippet shows how this is accomplished in the sample project:
private void GetListOfCurrencies()
{
txtMessage.Text = "Getting currencies..Wait";
App.CurrencyClient.GetCurrenciesCompleted +=
new EventHandler<GetCurrenciesCompletedEventArgs>
(CurrencyClient_GetCurrenciesCompleted);
App.CurrencyClient.GetCurrenciesAsync();
}
void CurrencyClient_GetCurrenciesCompleted(object sender,
GetCurrenciesCompletedEventArgs e)
{
if (e.Error != null)
{
txtMessage.Text = "Failed to get currencies list: "
+ e.Error.Message;
return;
}
txtMessage.Text = "Select currencies to get conversion rate";
_currencyList = e.Result as ObservableCollection<Currency>;
BindCurrencyDropDowns();
}
Since all network communications in Silverlight are asynchronous, a completion event is registered with call which gets calls when call returns. Once the application receives the lists, it binds two ComboBox
controls with list and enables the appropriate controls.
Get Conversion Rate
Now the ComboBox
controls are populated with the list of currencies. Pick from and to currency from these dropdowns and click on Convert It button. In click event of the button, I send a message to WCF service to get the conversion rate. The service exposes the Convert
method that takes the currency symbols as input parameters and returns conversion rate. The code in the sample project looks as below:
private void Submit_Click(object sender, RoutedEventArgs e)
{
btnSubmit.IsEnabled = false;
txtMessage.Text = "Getting conversion rate ...";
var fromCurrency = cbFromCurrency.SelectedItem as Currency;
var toCurency = cbToCurrency.SelectedItem as Currency;
App.CurrencyClient.ConvertCompleted +=
new EventHandler<ConvertCompletedEventArgs>(CurrencyClient_ConvertCompleted);
App.CurrencyClient.ConvertAsync(fromCurrency.Symbol, toCurency.Symbol);
}
void CurrencyClient_ConvertCompleted(object sender, ConvertCompletedEventArgs e)
{
btnSubmit.IsEnabled = true;
if (e.Error != null)
{
txtMessage.Text = "Failed to get conversion rate: "
+ e.Error.Message;
return;
}
if (e.status.StatusCode != 0)
{
txtMessage.Text = string.Format("[{0}]:{1} ",
e.status.StatusCode, e.status.StatusMessage);
if (string.IsNullOrEmpty(e.status.ExceptionDetails))
{
txtMessage.Text += e.status.ExceptionDetails;
}
return;
}
var fromCurrency = cbFromCurrency.SelectedItem as Currency;
var toCurency = cbToCurrency.SelectedItem as Currency;
double rate = e.Result;
txtMessage.Text = string.Format("{0} {1} = {2} {3}", 1,
fromCurrency.Symbol, rate, toCurency.Symbol);
}
Error Handling
When Silverlight application is communicating with a WCF service or for that matter for any network communication, there will be times when errors will occur. These errors could be any of the following:
- Network/Communication errors
- Security errors
- Application errors
Communication and Security errors will appear as appropriate exceptions or you will be able to look at Error
property of the completion arguments of async response. The most common cause of security type error will be due to missing CrossDomain
access policy file on server where WCF service is hosted. You can look at Silverlight Communication Diagnostics to get more details about how you can take care of a lot of communication or network related issues.
The tricky part is application exceptions that get thrown from WCF service or any other web services that Silverlight applications communicate with. If there is any exceptions thrown from the service, it appears as 404 status exception. For a moment, you will think that the Silverlight application could not connect to your service or there is something wrong with end point configuration. That is a possibility that you could explore. When you know that there is nothing wrong with the configuration and you can directly connect to WCF service from the browser, that would mean that WCF service application itself is throwing an exception or it's using exceptions to report errors to the caller. The only exception or error that Silverlight application is going to get in this case is a 404 (NotFound) exception. If you are writing WCF service for Silverlight application, then one thing you can do is to expose an OUT parameter for each method where you can fill in the status information and any messages that you want to communicate to the caller. I discussed this in the last article Currency Conversion WCF Service For Silverlight. In your completion method, you will receive that OUT parameter as a property on event argument. You can see from the following code snippet how this is used in the example project:
if (e.status.StatusCode != 0)
{
txtMessage.Text = string.Format("[{0}]:{1} ",
e.status.StatusCode, e.status.StatusMessage);
if (string.IsNullOrEmpty(e.status.ExceptionDetails))
{
txtMessage.Text += e.status.ExceptionDetails;
}
return;
}
In the next article, I will show how this WCF service will be extended to create a live currency rate monitor.