Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / WPF

Getting Started with Facebook Desktop (Client) Applications, C#, WPF / XAML and JSON

Rate me:
Please Sign up or sign in to vote.
4.92/5 (28 votes)
21 Jan 2009CPOL15 min read 319.1K   11.7K   139   78
A take on getting started with the Facebook API and WPF

Facebook_API/WPF_facebook1.gif

Table of Contents

  1. Introduction
  2. Background
  3. Prerequisites
  4. Running the Demo
  5. Using the Code
  6. Points of Interest
  7. 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

  1. 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!
  2. You will need a Facebook application to work against -
    1. Sign in to: Facebook and allow access to the Facebook Developer application when prompted.
    2. Click the "Set Up New Application" button near the top right
    3. Give the application a name, agree to the terms and click the "Save Changes" button
    4. You will need to set a call-back URL, I set mine to Google
    5. 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.

Talking to facebook!

Yellow1 Paste your Facebook application's API Key (see pre-requisites above) over [Your Key]
Yellow2 Paste your Facebook application's Secret (see pre-requisites above) over [Your Secret]
Yellow3 Give a number of friends to retrieve from Facebook (I used this for ease of use to limit the working set during testing)
Yellow4 Optionally save these settings
Yellow5 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.
Yellow5 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:

Solution Explorer

Orange1 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.
Blue2 Project: Facebook
Blue2YellowA

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.

    C#
     public Facebook.users.getInfo GetUsersInfo(double userId)
    {
        // Facebook parameter list (key value pair) that (needs to be
        // sorted in order to get the MD5 hash correct - see Facebook.Security
        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:
    • BuildFacebookPostString: Takes a Sorted Dictionary of parameters and generates a string of parameters that the Facebook API will recognise. This string will be pushed to the Facebook REST service for processing.
    • SendCallToFacebook: Adds common parameters for the API call (in some cases required and others ignored) to the parameters passed in. The call is then signed using GenerateFacebookParametersMD5Hash from the Facebook.Security class and the API call is made, passing the request down the wire. The response is checked for errors which are exposed via a simple throw. If all is well, the string of JSON retrieved from Facebook is returned. There was an option to use WCF here, but this would have added a more code and configuration, so to keep things simple and clean, I used the WebClient - I also think that there may have been some complications using a unified WCF approach as I had to do some string manipulation on certain results returned from the Facebook API, see JSON.Helpers class.

      Time permitting, I will change the implementation to WCF and post an updated version of the binaries.

Blue2YellowB 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.
Blue2YellowC 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
Blue2YellowD 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:

C#
// Namespace does not match code / folder hierarchy as I wanted it to reflect
// the facebook structure
namespace Facebook.users
{
    /// <summary>
    /// Type that can be serialized from JSON to a .net class
    /// http://wiki.developers.facebook.com/index.php/Users.getInfo
    /// </summary>
    [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:

C#
// Property to return the time in a .net friendly datetime instead of the unix EPOC time
[IgnoreDataMember] // Marked as ignore as it is not to be populated from the facebook
// lookup(i.e. not a facebook field)
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.

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

Blue3 Project: FacebookApplication
Blue3YellowA 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.

XML
<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:

XML
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">

Textboxes and Settings

The textboxes are data bound to the application settings and the binding is done on the textbox element:

XML
<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:

C#
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:

XML
<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 :)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United Kingdom United Kingdom
You can read more about me here:

http://uk.linkedin.com/in/murrayfoxcroft

Comments and Discussions

 
QuestionApplication key issue Pin
amuzit@gmail.com6-Nov-19 1:58
amuzit@gmail.com6-Nov-19 1:58 
QuestionConfused as heck. Pin
ancich4-Nov-13 16:49
ancich4-Nov-13 16:49 
QuestionNot working for already login second time Pin
npdev1317-May-13 20:45
npdev1317-May-13 20:45 
AnswerRe: Not working for already login second time Pin
Murray Foxcroft23-May-13 10:55
Murray Foxcroft23-May-13 10:55 
QuestionDemo API key Pin
Member 1001110824-Apr-13 11:45
Member 1001110824-Apr-13 11:45 
AnswerRe: Demo API key Pin
Murray Foxcroft24-Apr-13 21:35
Murray Foxcroft24-Apr-13 21:35 
Questionwant to get friend's status and comments Pin
faizan _ ahmed2-Mar-13 9:11
faizan _ ahmed2-Mar-13 9:11 
AnswerRe: want to get friend's status and comments Pin
Murray Foxcroft4-Mar-13 0:53
Murray Foxcroft4-Mar-13 0:53 
QuestionDeserialzing error Pin
Mina Pansuriya20-Jan-12 21:06
Mina Pansuriya20-Jan-12 21:06 
I am getting following error after facebook login window is closed (When it gives message - You may now close this window and return to the application.)

There was an error deserializing the object type Facebook.auth.getSession. The value '100000052404170' can not be parsed as the type 'Int32'.

Kindly Help
AnswerRe: Deserialzing error Pin
Murray Foxcroft22-Jan-12 7:26
Murray Foxcroft22-Jan-12 7:26 
AnswerRe: Deserialzing error Pin
Member 993350810-Apr-13 5:12
Member 993350810-Apr-13 5:12 
GeneralRe: Deserialzing error Pin
Murray Foxcroft10-Apr-13 6:05
Murray Foxcroft10-Apr-13 6:05 
Questionmy vote is 5 Pin
mohamad yousef15-Jan-12 1:51
mohamad yousef15-Jan-12 1:51 
AnswerRe: my vote is 5 Pin
Murray Foxcroft15-Jan-12 21:42
Murray Foxcroft15-Jan-12 21:42 
GeneralExcellent! Just what I need for some ideas I have.. Small problem though.. Pin
seesharpme19-Apr-11 2:05
seesharpme19-Apr-11 2:05 
GeneralRe: Excellent! Just what I need for some ideas I have.. Small problem though.. Pin
Murray Foxcroft19-Apr-11 11:42
Murray Foxcroft19-Apr-11 11:42 
GeneralRe: Excellent! Just what I need for some ideas I have.. Small problem though.. Pin
seesharpme20-Apr-11 0:41
seesharpme20-Apr-11 0:41 
GeneralRe: Excellent! Just what I need for some ideas I have.. Small problem though.. Pin
Murray Foxcroft20-Apr-11 9:22
Murray Foxcroft20-Apr-11 9:22 
GeneralProxy Pin
Lee Weston14-Nov-10 21:46
Lee Weston14-Nov-10 21:46 
GeneralRe: Proxy Pin
Murray Foxcroft14-Nov-10 22:05
Murray Foxcroft14-Nov-10 22:05 
GeneralMy vote of 5 Pin
Martin Welker19-Oct-10 2:04
Martin Welker19-Oct-10 2:04 
Generalwant to save the friends list locally to make it avilable offline Pin
moban4u11-Oct-10 22:14
moban4u11-Oct-10 22:14 
GeneralRe: want to save the friends list locally to make it avilable offline Pin
Murray Foxcroft11-Oct-10 23:16
Murray Foxcroft11-Oct-10 23:16 
Generallogin works.but Pin
moban4u30-Sep-10 18:34
moban4u30-Sep-10 18:34 
GeneralRe: login works.but Pin
Murray Foxcroft30-Sep-10 21:08
Murray Foxcroft30-Sep-10 21:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.