Table of Contents
- Introduction
- Background
- Prerequisites
- Running the Demo
- Using the Code
- Points of Interest
- History
Introduction
I was looking to get my head around Windows Presentation Foundation (WPF) and had also been intrigued by the Facebook API, accessible via a RESTful service. I had planned to use a unified Windows Communication Foundation (WCF) approach to access the API, but this added complexity which is explained in the article below. Instead, I took the pieces from .NET 3.51 that made the most sense and provided the simplest solution I could find, using the WebClient
object, data contracts and the JSON serializer that comes with .NET 3.51. What transpired was a handy Facebook application that others may find useful, so I wrote this article.
Background
I looked around at a few options for connecting Facebook to C# and there are some fantastic community efforts. What I did not find was a simple, working primer to get one started with building C# Facebook applications as well as understanding how the API works. This application provides such, without the complexities of many layers of abstraction, convoluted exception handling or hundreds of classes; hopefully just enough to get you pointed in the right direction.
Prerequisites
- The .NET Framework 3.51 is required before you run this sample. If you do not have it, please download and install from Microsoft. I used the installer located here, unfortunately a 232MB behemoth!
- You will need a Facebook application to work against -
- Sign in to: Facebook and allow access to the Facebook Developer application when prompted.
- Click the "Set Up New Application" button near the top right
- Give the application a name, agree to the terms and click the "Save Changes" button
- You will need to set a call-back URL, I set mine to Google
- Keep the API Key and Secret handy, you will need these to run the application
Running the Demo
Extract the demo from the archive and double click FacebookApplication.exe. The following diagram and corresponding points will walk you through the user interface.
| Paste your Facebook application's API Key (see pre-requisites above) over [Your Key] |
| Paste your Facebook application's Secret (see pre-requisites above) over [Your Secret] |
| Give a number of friends to retrieve from Facebook (I used this for ease of use to limit the working set during testing) |
| Optionally save these settings |
| Click "Login" and sign in to Facebook. You will need to allow access to the Facebook application on the first run and will be prompted to do so. Close the login window when instructed. |
| Click "Get Friends" to call in to the Facebook API and watch the application populate the user interface. |
Using the Code
The first thing you should do when opening the solution files (source code) is build the solution so that the project references resolve properly and the XAML can be viewed in the designer. The code is verbosely commented, so I will not go into infinite detail, but I will endeavour to explain the general architecture and project structure to ramp up your understanding. I had also considered a more Object Orientated approach and leveraging base classes to allow for a more elegant design that would scale better, but opted out of this to ensure that the solution is simple and easily followed.
The following diagram and corresponding points will walk you through the solution files:
| The solution supplied, WPF_Facebook , contains two projects:
Facebook : a class library containing all interaction, helpers, windows and coding against the Facebook API FacebookApplication : a simple Windows Presentation Foundation (WPF) project to demonstrate how the Facebook project should be consumed.
|
The Code folder contains the bulk of the code to leverage the Facebook API.
- Facebook.API.cs: This is a
public
facing class that orchestrates the rest of the project to call in to the Facebook
API and return the desired information. The constructor initiates a new session (sessionInfo = new Facebook.auth.getSession();
) which then exists for the duration of the API integration. You may have noted the strange naming convention Facebook.auth.getSession
, this makes sense when you inspect the Facebook
API Auth.getSession, where my code tries to closely mimic the relevant Facebook APIs in play.
Also included in this file are two Facebook
API calls, public Facebook.users.getInfo GetUsersInfo(double userId)
and public ArrayList GetListOfFriends()
I will talk through the first one to explain how API calls are orchestrated in this class library.
public Facebook.users.getInfo GetUsersInfo(double userId)
{
var parameters = new SortedDictionary<string, string>();
parameters.Add("method", "facebook.users.getinfo");
parameters.Add("session_key", sessionInfo.session_key);
parameters.Add("uids", userId.ToString());
parameters.Add("fields", "first_name,last_name,pic_big,pic_small,profile_url,
status,hs_info,hometown_location");
string jsonResult = JSON.Helper.StripSquareBrackets(
Facebook.Web.SendCallToFacebook(parameters));
return (JSON.Helper.Deserialize<Facebook.users.getInfo>(jsonResult));
}
Parameters are added to a sorted dictionary collection, then the API call is executed, returning a string
of JavaScript Object Notation (JSON). This JSON string
is deserialized into a .NET class which can then be worked with as you would any normal .NET code. Due to nuances in Facebook JSON deserialization, I had to create the helper JSON.Helper.StripSquareBrackets
, which tailors the JSON returned from Facebook into a format that can be processed by Microsoft's DataContractJsonSerializer
(.NET 3.5).
Like with the session, the C# return type reflects the Facebook API call - see Users.getInfo.
- Facebook.Helpers.cs: Only one method in here,
ConvertUnixTimestampToDotNet
, which converts the elusive "unix" date into a .NET friendly one. - Facebook.Login.cs: Until you figure out how it works, logging in to Facebook can be confusing. The code is well documented and the following will help you to understand the big picture:
- Read the Facebook docs first - How Facebook Authenticates Your Application
- It is as "easy" as 1 - Create an Auth Token, 2 - Perform the login, 3 - Keep track of the session for use in the API calls.
- For Facebook "security reasons", you always need to log in through the Facebook login page out on the web. What this means is that you need to open a web browser or a window with a web browser control on it (See
Facebook.Windows.Login.XAML
) and then pass through a URI formatted with the right details - the URI parsing for the login sits in this class - see: GetFacebookLoginPage(string auth_token)
. There is no way that I can tell of to perform a silent login without trying to bodge it behind the scenes.
- Facebook.Security.cs: This class contains the method
GenerateFacebookParametersMD5Hash<string, string>
. Facebook uses a RESTful API which calls across the internet. A shared secret is used to make sure that the information leaving the client machine is the exact same information that arrives at the Facebook server. Essentially, MD5 is used to sign the client application's outgoing data and this is verified by Facebook when it arrives at their server. Both Facebook and the client application will have a copy of the shared secret, but the secret is never required to be passed over the wire during API calls, thus avoiding interception. (More information on shared secrets at this Wikipedia link).
In order to ensure the MD5 hash yields the same result on the client and Facebook server, the parameters need to be sorted before being hashed - this is why I have stored them in a sorted dictionary collection.
- Facebook.Web.cs: This class contains methods for HTTP based communications between a client and the Facebook server:
| The Windows folder contains all WPF windows required to successfully leverage the facebook API: |
Facebook.Windows.Login.XAML
: A simple XAML window that contains a WPF Frame control which can host a web browser control. Facebook requires login to be done using their web front end. This can be frustrating, but if the URI for the Facebook call contains the right format and valid parameters, Facebook will take care of logging in and checking credentials, providing a full interface for confirming access to the targeted Facebook application.
When this page is loaded through the constructor, it takes a URI as a parameter. This URI needs to be properly formatted and parameterised for Facebook login and the window's Frame
control immediately navigates to the address.
The page takes a few seconds to load and I thought, to indicate activity, I would display a wait indication - a XAML clock face spinning.
Facebook.Windows.WaitPage.XAML
: The wait clock mentioned above. It is irrelevant to the Facebook
API integration, but is a simple sample of WPF animation. This did not work out as well as I had expected as there are still some "blank" periods while the XAML clock loads and switches. I thought I would leave it in the solution as it is an interesting little XAML page that may be of interest.
| The JSON folder was planned to hold parsing classes for JSON, but in leveraging .NET 3.5, I saved myself a lot of effort. This boiled down to the simple methods below. |
JSON.Helper.cs: contains the following methods:
Serialize<T>(T typeToserialize)
: Serializes a .NET object to a string
of JSON T Deserialize<T>(string jsonToDeserialize)
: Deserializes a .NET object from a string
of JSON string StripSingleResult(string jsonIn)
: Used to parse a result from Facebook that contains only a single value ArrayList ArrayFromJSON(string jsonIn)
: Parse out an array of items from a string
of JSON string StripSquareBrackets(string jsonIn)
: Removes superfluous square brackets [ and ] that Facebook returns on some calls. If these are not removed, the JSON string
will not deserialize with Microsoft's DataContractJsonSerializer
| The Types folder contains structures to hold data returned from Facebook. |
This is where the code "seems" to get messy - it is debatable if I should have used Facebook.Types
as the namespace. I decided to opt for a structure that closely mimics the Facebook
API and, although slightly less readable from a .NET perspective, it makes easy work of referencing the portion of Facebook
API that you are working against. The naming mirrors the Facebook
API calls. Windows Communication Foundation (WCF) DataContract
and DataMember
attributes are used to decorate the properties and classes so that they can be serialized and deserialized by the DataContractJsonSerializer
. This handy feature helped shave off a lot of development time and provided a very elegant coding solution to processing the JSON results returned by Facebook.
The code is very simple and one of the contracts looks as follows:
namespace Facebook.users
{
[DataContract]
public class getInfo
{
[DataMember]
public string first_name { get; set; }
[DataMember]
public string last_name { get; set; }
...
...
The serializer and deserializer are aware of how to work with these contracts without any further coding. The IgnoreDataMemberAttribute
is also used to exclude computed and additional fields from getting in the way of the serializer and provides an uncluttered, useful mechanism to extend the classes beyond the fields that Facebook returns. An example of this would be exposing a Facebook
date in a format that .NET can readily consume:
[IgnoreDataMember]
public DateTime time_DateTime { get {
return Facebook.Helpers.ConvertUnixTimestampToDotNet(time); } }
These WCF attributes are powerful and there is support for nested DataContracts
(i.e., a DataContract
within a DataContract
). To my surprise, this worked the first time with the DataContractJsonSerializer
. A good example of this is the hometown_location
nested in the getInfo
class in the Facebook.users
namespace.
All of this goodness can be found in the .NET System.Runtime.Serialization
namespace.
| The app.config and Settings file (namespace Facebook.Properties ) hold the required settings, access and persistence for the following items: |
ApplicationKey
: The Facebook
application key of the application to connect to Secret
: The Facebook
secret of the application to connect to ApiVersion
: The version of the Facebook
API to connect to ApiUrl
: The URL of the Facebook
RESTful API. Parameter substitution (e.g. of {0}) is used to complete construction this string in code LoginUrl
: The Facebook
login URL to use. Parameter substitution (e.g. of {0}) is used to complete construction of this string in code MaxFriends
: Maximum number of friends to retrieve, mainly used to reduce the time taken for testing by working with a smaller set than "all friends"
Note: The settings are persisted per user and are stored (by default by .NET) in the user's C:\Users\<username>\AppData\Local\FacebookApplication folder.
Project: FacebookApplication |
| This project contains a single WPF/XAML form, FacebookFriendsList . On the form, you will find the buttons and fields as described in Running the Demo. Below these controls is an open area that contains a standard WPF ListView control. |
Grid Layout
The window uses a grid layout set to stretch to the size of the window. By using as little absolute dimensioning of window controls as possible, it is easy to generate a window that can resize fluidly and scale well.
<Grid HorizontalAlignment="Stretch" x:Name="uiGridMain" VerticalAlignment="Stretch">
Default Styles
I have used default styles for buttons, textboxes and labels to reduce duplication and clutter in the XAML. These default styles apply across the entire window and an example can be found if you search for one:
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
Textboxes and Settings
The textbox
es are data bound to the application settings and the binding is done on the textbox
element:
<TextBox Grid.Row="0" Grid.Column="1" x:Name="uiTbxApplicationKey" TabIndex="0"
Text="{Binding Path=ApplicationKey, Mode=TwoWay,
Source={x:Static p:Settings.Default}}"/>
This declarative binding requires no "code," just configuration through XAML. The Save Settings button makes a single static call to the Facebook
assembly to save the bound settings. (Facebook.Properties.Settings.Default.Save();
)
Login Button
The Login button's click event maps to private void uiBtnLogin_Click(object sender, RoutedEventArgs e)
in the code behind. Firstly, it performs simple validation on the parameter lengths, primarily to ensure that the user has changed them from the defaults to correct (at least in length) values. The API from the Facebook
project is then instantiated and the constructor in the API initiates the login to Facebook. A login failure results in an exception, so an assumption can be made on the following line that the login was successful. On success, the uiBtnGetFriends
button is enabled so that the user can execute this action.
As discussed, failure to log in results in an error thrown and this is caught and displayed to the user and recorded on the status bar.
Note: The Facebook
API references private Facebook.API fbi;
is held at the class level. This is because it encapsulates a session with the Facebook
server and can be re-used for every call in to the API, there is no need to instantiate every time a call is required.
Get Friends Button
The Get Friends button's click event maps to private void uiBtnGetFriends_Click(object sender, RoutedEventArgs e)
in the code behind and, apart from setting some status messages, performs two main functions.
Firstly, it calls through to the Facebook
API and retrieves an ArrayList
of Facebook
user IDs of the current user's friends. See ArrayList facebookFriends = fbi.GetListOfFriends();
Secondly, the method iterates through the ArrayList
of IDs and retrieves more details for each one of these, up to a maximum of Facebook.Properties.Settings.Default.MaxFriends
friends. The API call fbi.GetUsersInfo
is used to do this and each resulting Facebook.users.getInfo
object (essentially the 'equivalent' of a Facebook
'user
' entity) is stored in an ObservableCollection
. The beauty of this collection object is that it will automatically notify a UI of changes in the collection and the UI will automagically update. In this project, it causes the list to visibly grow with each user retrieved from Facebook
and added. Fancy!
List View and List View Items
The ListView
is populated by using an ItemTemplate
. In this case, I have used a StackPanel
to produce a composite control to encapsulate a subset of a Facebook
user's details and this ItemTemplate
is bound to the collection here:
var facebookFriendsDetails = new ObservableCollection<Facebook.users.getInfo>();
uiLvFriends.ItemsSource = facebookFriendsDetails;
It is interesting to see that the binding is done before data is added, instead of the typical process of retrieving data and then binding this data to a list or grid.
The ItemTemplate
below is rendered for every Facebook
friend returned and a nice enhancement would be to move this out into a custom XAML user control:
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel FlowDirection="LeftToRight" Orientation="Horizontal">
<Image Margin="3" Source="{Binding Path=pic_small}" />
<StackPanel Margin="3" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal">
<TextBlock>
<Hyperlink Click="NavigateToUserProfile"
NavigateUri="{Binding Path=profile_url}">
<InlineUIContainer>
<TextBlock FontSize="12" FontWeight="Bold"
Text="{Binding Path=full_name}" />
</InlineUIContainer>
</Hyperlink>
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock FontStyle="Italic" Text="Status: "/>
<TextBlock Text="{Binding Path=status.status_line}" />
</StackPanel>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
One last item to note, the hyperlink element's click event is mapped to the NavigateToUserProfile
method in the code behind, allowing the application user to click through to the relevant Facebook
friend's profile, which opens in the client's default browser.
Points of Interest
- Connecting to the
Facebook
RESTful API seemed easiest through the WebClient
class and JSON serialized objects. A unified WCF approach could replace this and would make for an interesting investigation, contrasting the additional code and configuration required vs. potential benefits. - The more I use WPF, the more I like it - there is so much there already (half of which I'll never understand...) and still so much more I would like from it (e.g., proper visual layers)
- Again, with "WPF layers", there appears to be no explicit way to
zOrder
controls, they appear to order in the sequence that they appear in the XAML - Animation in WPF is easier than you may think - although it does make you dust off your Math (Logo?) skills :)
- Less in WFP is often more - the less you hard code dimensions of items, the better the design will scale to different screen resolutions and resizing
- The
Facebook
API seems to have a bad reputation, but once you understand it, the API is robust and reliable - The
Facebook
API appears to change regularly, part of the reason I limited the scope of this example to a simple friends lookup, hoping it will remain "current" for some time - The
Facebook
API documentation is well worth a read before you begin, this and Google answered most of my questions
History
- V1.0 - no changes... yet :)