(转) Write Silverlight Application To Consume Currency Converter WCF Service
In 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 Silverlight application that will consume WCF service. Following image shows how the UI for the application looks like.
As you can see, a very simple layout. There are 2 dropdown combo boxes that contain list of currencies available. And a button that you can click to submit selected currencies to get conversion rate.
Adding Reference To WCF Service
Since the whole application depends on communication with WCF service that serves currency conversion rates, first thing that needs to be done is to add 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 reference to WCF service that we created. Following images show the process of adding service reference.
Populating Silverlight ComboBox
There are two input data points for this Silverlight control or applications. A source (from) and target (to) currency for which you want to get conversion rate. The application itself does not contain that list. This list comes from 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. Lets look at XAML mark up 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;
// Make button clickable now.
cbToCurrency.IsEnabled = cbFromCurrency.IsEnabled =
btnSubmit.IsEnabled = _currencyList.Count > 0;
}
When 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. 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 application receives the lists, it binds 2 ComboBox controls with list and enables appropriate controls.
Get Conversion Rate
Now the ComboBox controls are populated with 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 message to WCF service to get conversion rate. The service exposes Convert method that takes the currency symbols are input parameters and returns conversion rate. The code in sample project sample 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
- Aplication 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. 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 lot of communication or network related issues.
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 exception thrown from service, it appears as 404 status exception. For a moment you will think that silverlight application could not connect to your service or there is something wrong with end point configuration. That is a possibility that could explore. When you know that there is nothing wrong with configuration and you can directly connect to WCF service form browser, that would mean that WCF service application itself is throwing exceptin or its using exceptions to report errors to the caller. 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 caller. I discussed this in 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 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 next article I will show how this WCF service will be extended to create a live currency rate monitor.