GitHub (Lib and Demo) and Short Teaser Video
Introduction
Actually, I've never needed an API to do currency conversions, but I've just found these services provided by Yahoo YQL interesting and so I built an API to wrap this.
This is a .NET CORE 2 API written in C#(7) thought to be multi-platform and usable in mobile development (through Xamarin Forms and etc).
Think about this API as an Object Model for the Yahoo Currency API. It encapsulates all the data and lets you to work with it more naturally.
The most interesting thing is that it implements Async support and also INotifyPropertyChanged
. That means, you can declare it and bind it to XAML, in a very nice way.
This project and a WPF demo is completely available on GITHUB.
This is the first release of this project, I did many tests and it worked very well. As I'm sharing this on GITHUB, the main goal is to improve it, as possible, with the hand of everyone. So, if you find an unexpected behavior or a bug, please don't vote down. Fork it or just let me know, let's improve it together.
Background
The main thing here is about the Yahoo Query Language or, in short words, YQL!
What is YQL? According to Wikipedia:
Quote:
Yahoo! Query Language (YQL) is an SQL-like query language created by Yahoo! as part of their Developer Network. YQL is designed to retrieve and manipulate data from APIs through a single Web interface, thus allowing mashups that enable developers to create their own applications.
Initially launched in October 2008 with access to Yahoo APIs, February 2009 saw the addition of open data tables from third parties such as Google Reader, the Guardian, and The New York Times. Some of these APIs still require an API key to access them. On April 29 of 2009, Yahoo introduced the capability to execute the tables of data built through YQL using JavaScript run on the company's servers for free.
So, in short, I've just managed to write a simple and clear code to "talk" with Yahoo, through the YQL, asking for the updated currency quotes.
You can obtain the updated currency quotes easily by accessing this link. As you see, it returns an XML document with the updated quotes for all the currencies available!
So, what I've done is just send a web request to that web address, get the response and parse it! Way simple!
I'm not going to explain abount YQL, this is just pointless. You can find the complete documentation, samples and more on the official site:
YQL - Yahoo Developer Network
Using the Code
There are, basically, two ways of coding with this library. The first one is through the code behind (C# or VB.NET) in an asynchronous or synchronous way as you can see below:
public static async Task AsyncExample()
{
HL.YahooCurrency.Currency currency = new HL.YahooCurrency.Currency();
await currency.WaitLoad();
currency.SourceUnit = currency.Units.ElementAt(0);
currency.OutputUnit = currency.Units.ElementAt(1);
currency.SourceValue = 2.32f;
System.Diagnostics.Debug.WriteLine(currency.OutputValue);
}
public static void SynchronousExample()
{
HL.YahooCurrency.Currency currency = new HL.YahooCurrency.Currency();
currency.WaitLoad().Wait();
currency.SourceUnit = currency.Units.ElementAt(0);
currency.OutputUnit = currency.Units.ElementAt(1);
currency.SourceValue = 2.32f;
System.Diagnostics.Debug.WriteLine(currency.OutputValue);
}
It is very important to mention that on both synchronous and asynchronous contexts, you MUST invoke the method WaitLoad()
. Why? Simple!
As you well know, we can't declare an async
constructor and this just sounds crazy! Anyway, the constructor of the class Currency
invokes a async Task
to make a web request and parse the data in a way that prevents the user interface from freezing while waiting for this process (That takes between 1 to 5 seconds, depending on the user network connection speed).
The main thing here is: The WaitLoad()
method returns the running task created for initialize the Currency
object in background. So, as you might have thought, it is trivial to wait for that task to end. If you are going to use async
, you just need to await
the WaitLoad()
. If you are not in an async
context, you shall block the executing thread while the background task is still running. Both scenarios are coded above, as you can see.
The second way, is directly on XAML:
<Window x:Class="Yahoo_Currency_WPF_DEMO.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Yahoo_Currency_WPF_DEMO"
xmlns:yahoo="clr-namespace:HL.YahooCurrency;assembly=HL.YahooCurrency"
mc:Ignorable="d"
Title="HL.YahooWeather WPF DEMO" Height="350"
Width="600" WindowStartupLocation="CenterScreen">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<local:InvertBoolConverter x:Key="InvertBoolConverter"/>
<local:BytesToBitmapImageValueConverter x:Key="BytesToBitmapImageValueConverter"/>
<local:StringToDoubleConverter x:Key="StringToDoubleConverter"/>
</Window.Resources>
<Window.DataContext>
<yahoo:Currency/>
</Window.DataContext>
<Grid IsEnabled="{Binding IsLoading,
Converter={StaticResource InvertBoolConverter}, Mode=OneWay}">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<GridSplitter HorizontalAlignment="Center"
Margin="0" Grid.ColumnSpan="2"
Width="1" Background="Black"/>
<Label Content="Input" HorizontalAlignment="Center"
Margin="0,15,0,0" VerticalAlignment="Top" FontSize="18.667"/>
<Label Content="Output" HorizontalAlignment="Center"
Margin="0,15,0,0" VerticalAlignment="Top"
FontSize="18.667" Grid.Column="1"/>
<TextBox Height="23" Margin="10,102.6,10,0"
TextWrapping="Wrap" Text="{Binding SourceValue,
BindsDirectlyToSource=True, Converter={StaticResource StringToDoubleConverter},
IsAsync=True, Mode=OneWayToSource, ValidatesOnNotifyDataErrors=False,
UpdateSourceTrigger=PropertyChanged, TargetNullValue=0}"
VerticalAlignment="Top" Width="238.3"
PreviewTextInput="TextBox_PreviewTextInput" MaxLength="16"/>
<ComboBox Margin="10,75.64,10,0" VerticalAlignment="Top"
Width="238.3" Grid.Column="1" ItemsSource="{Binding Units}"
SelectedIndex="0" SelectedValue="{Binding OutputUnit,
Mode=OneWayToSource}" IsSynchronizedWithCurrentItem="False"
Loaded="ComboBox_Loaded"/>
<ComboBox Margin="10,75.64,10,0" VerticalAlignment="Top"
Width="238.3" ItemsSource="{Binding Units}"
SelectedIndex="0" SelectedValue="{Binding SourceUnit,
Mode=OneWayToSource}" IsSynchronizedWithCurrentItem="False"
TabIndex="2147483646" MinWidth="10" Loaded="ComboBox_Loaded"/>
<TextBox Height="23" Margin="10,102.6,10,0"
TextWrapping="Wrap" Text="{Binding OutputValue, Mode=OneWay,
StringFormat=N2}" VerticalAlignment="Top" Width="238.3"
Grid.Column="1" IsReadOnly="True"/>
<GroupBox Header="Information" Margin="10,130.6,10,10">
<Grid>
<Image HorizontalAlignment="Left" Height="55"
VerticalAlignment="Top" Width="88"
Source="{Binding SourceUnitInfo.Flag,
Converter={StaticResource BytesToBitmapImageValueConverter}}"/>
<Label Content="{Binding SourceUnitInfo.Country,
Mode=OneWay}" HorizontalAlignment="Left"
Margin="93,15.04,0,0" VerticalAlignment="Top"/>
<Label Content="Currency: "
HorizontalAlignment="Left" Margin="10,60,0,0"
VerticalAlignment="Top"/>
<Label Content="Currency Symbol:"
HorizontalAlignment="Left" Margin="10,90.96,0,0"
VerticalAlignment="Top"/>
<Label Content="{Binding SourceUnitInfo.Currency,
Mode=OneWay}" HorizontalAlignment="Left"
Margin="77.87,60,0,0" VerticalAlignment="Top"/>
<Label Content="{Binding SourceUnitInfo.Symbol, Mode=OneWay}"
HorizontalAlignment="Left" Margin="112.376,90.96,0,0"
VerticalAlignment="Top"/>
</Grid>
</GroupBox>
<GroupBox Header="Information" Margin="10,130.6,10,10" Grid.Column="1">
<Grid>
<Image HorizontalAlignment="Left" Height="55"
VerticalAlignment="Top" Width="88"
Source="{Binding OutputUnitInfo.Flag,
Converter={StaticResource BytesToBitmapImageValueConverter}}"/>
<Label Content="{Binding OutputUnitInfo.Country,
Mode=OneWay}" HorizontalAlignment="Left"
Margin="93,15.04,0,0" VerticalAlignment="Top"/>
<Label Content="Currency: "
HorizontalAlignment="Left" Margin="10,60,0,0"
VerticalAlignment="Top"/>
<Label Content="Currency Symbol:"
HorizontalAlignment="Left" Margin="10,90.96,0,0"
VerticalAlignment="Top"/>
<Label Content="{Binding OutputUnitInfo.Currency,
Mode=OneWay}" HorizontalAlignment="Left"
Margin="72.87,60,0,0" VerticalAlignment="Top"/>
<Label Content="{Binding OutputUnitInfo.Symbol,
Mode=OneWay}" HorizontalAlignment="Left"
Margin="112.376,90.96,0,0" VerticalAlignment="Top"/>
</Grid>
</GroupBox>
<Border BorderThickness="1" Grid.ColumnSpan="2"
Background="WhiteSmoke" Visibility="{Binding IsLoading,
Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}">
<Grid>
<TextBlock Text="Loading Data" VerticalAlignment="Center"
HorizontalAlignment="Center" FontSize="22"/>
<ProgressBar HorizontalAlignment="Center" Height="10"
VerticalAlignment="Center" Width="140"
Margin="0,54,0,0" IsIndeterminate="True"/>
</Grid>
</Border>
</Grid>
</Window>
In short, I'm declaring the Currency
object as the Data Context
in this window. Also, the Currency
class has a bindable property called IsLoading
. When any request or data parsing operation is going on in the background, this property will be set to true
till the end of the task! This enables you to create a responsive and smart UI for your applications.The coolest things (I think) come now!!!
Special Features
I've embedded a database on this API that will return interesting information about all the currencies.
This information is:
- The name of the country from where this currency is.
- A flag picture of the currency country.
- The unicode symbol of the currency.
- The currency code.
In fact, this is not just a wrapper around Yahoo Services. It's more like a solution for currency conversion. Also, there's a method called UpdateAsync()
that allows you to update the Currency
object you are working with.
Points of Interest
The most interesting thing here, in my viewpoint, is the asynchronous initialization of an object. In fact, this is a very interesting approach.
Note that, on the XAML code of the WPF demo, there is a commented line about ~6% CPU usage. In fact, I've found that when you hide a progress bar control on WPF (at least here), it keeps draining CPU time to draw the progress bar, even though it's hidden! A bug on Windows Presentation Foundation?
I'd like to mention that I've also used some interesting value converters on this demo. The most interesting is the InvertBoolConverter
. It can be used when you need to bind to a bool
property and want to get that value inverted. For example, if you want to bind the IsEnabled
property of some control to the IsLoading
property of Currency
. You might want to keep the IsEnabled
to false
when IsLoading
is true
. This is the trick!
API Documentation
Before you ask me, let me explain it.
All the classes, methods and properties of this API are summarized. So, you are going to get some help when coding.
Even though I know that would be nice to build a little documentation, by now I'm busy with some other things and don't have time for that :/.
However, as the API is very simple, and there is a demo, I believe you will not have any trouble to code with it!
Another Useful API
You can find on my GITHUB another wrapper for another Yahoo's services. This one is related to Weather.
It's an .NET Core 2 API, written like the Currency
one, for getting forecast information. It also supports async
, data binding, etc. I pretend to write an article/tip about it in the near future!
History
- 18/11/2017 - Tip published