The wFace – windows phone 7 facebook integration Series
This article series shows how to use the Facebook graph_API (http://developers.facebook.com/docs/API) in a windows phone 7 (wp7) Silverlight application. All used tools are free software - for downloads take a look at the links at the end of this document.
Part1 Part2 The Idea behind
This article shows how to use the Facebook graph_API (http://developers.facebook.com/docs/API) in a windows phone 7 (wp7) Silverlight application. All used tools are free software - for downloads take a look at the links at the end of this document.
We made a .NET iPhone application using MonoTouch. The thing is a Rock Paper Scissors game (RPS).
A well known game I would say. The special thing about our implementation is a tight Facebook integration.We hold a game server to store high-scores and allow internet playing. Instead of handling user management ourselves we use Facebook to do this.
Further we post results (new rank reached, game won / lost) on the user's Facebook wall.
Since the code is .NET it looks as if a port from iPhone to wp7 is an easy task. But there are some problems and differences between the two platforms. A part of the problems may disappear after wp7 (beta at the moment) reaches RTM.
The sample - and what’s missing
This sample consists of two parts. In part one I'll cover authentication. Part2 will be about data retrieval posting data to Facebook.
Missing things:
- UIX. I'll use simple stiles to focus on content. The nature of Silverlight projects allows you to make the things looking cool without the need to "recode" anything.
- Excessive Error handling. In a real world application there would be "error pages" which provide meaningful user information. In this project we just show the plain error messages.
Prerequisites
To build / run the project you'll need:
- The Windows phone developer tools (free)http://developer.windowsphone.com/
- A Facebook account
- A Facebook application. You can create an application here: http://www.facebook.com/developers/ . I'll explain later how to setup an application.
- Some experiences with Silverlight and .NET development. I'll only focus wp7 specific Silverlight topics. There are tons of good Silverlight articles on the web to get the basics.
The “project”
Our project will have 2 screens, one "Main Screen" and a "Login Screen". In wp7 such screens are (there are other options to) "Pages".
Facebook integration - a closer look
The Facebook application
A Facebook application is a piece of information which interacts with the user. There are different types of application but they share some common information.
First of all (and primarily important for us) imagine a Facebook application as a kind of contract between you (the developer) and the user (Facebook account owner). You identify yourself via properties of the application and further (via code) you ask for permissions.
In our case we want to several things:
- We want to get some information from the users profile
- We want to post in the name of the user
Authentication
The steps (from the user's perspective) look like this.
- Our application sends the users to the Facebook login page. Assume the users enters valid credentials there.
- The user is redirected to the "application permissions" page http://developers.facebook.com/docs/authentication/
- The user is redirected to a special page. At this point we get some kind of credentials for further use. This is a "token" which we further use to make calls to the Facebook API.
Creating your Facebook application
To create your first Facebook application login with your Facebook account and visit http://www.facebook.com/developers/ - this is an application and you see a dialog as explained in paragraph 2 above.
Figure 1: Developer Application Permissions
After you allow the application access to your Facebook account you'll be redirected to the developers home page. Near the top (right side in main content) is a button "+Set up new application". Click this to create a new application.
Figure 2: Create application part 1
Enter a unique Name and click "Create Application". An error may occur - ignore it (if it is not "Name not allowed") and follow the next steps.
Go back to http://www.facebook.com/developers/ and notice that below the "+Set up new application" button is a list of your applications. Click on your newly created application and make a note of the two fields "Application ID" and "Application Secret". You need these two values later in your wp7 application.
Figure 3: Application Settings
IMPORTANT: those two values are confidential! Do not post them like I do here. I'll use this application for the article and delete it later. So again: please do never publish these values!
NOTEDo not change the application settings (unless you know how the things work) - the default is perfect to use it in this sample.
Create the wp7 application in Visual Studio 2010 (Express)
Create a new project
Don't worry - later I'll explain in more details what's going on behind the scenes. But at the moment just follow this guide. Select "File/New Project" from the menu to get the following dialog:
Figure 4: New Project Dialog
In this case we use a simple "Windows Phone Application". Select a directory and give the thing a meaningful name. In the next step you should see a XAML file and a preview of your application.
Authentication - a deeper look
How it works
Facebook uses OAuth 2.0 -the details are described here http://developers.facebook.com/docs/authentication/.
Normally we would use "Desktop authentication" http://developers.facebook.com/docs/authentication/desktop but due to a bug in the wp7 beta bits we have to take a different approach.
In simple words the things work like this: we provide the user a webpage (the page comes from Facebook) and there he enters his credentials. We'll never get access to the credential values. Further we pass our "Application ID" (see above) to this page and last not least we inform the page about the access we need.
The user enters his credential and Facebook redirects him to a page where the user must allow our application to access the properties / operations. There are a lot of possible values for allowance. You can find them on this page: http://developers.facebook.com/docs/authentication/permissions.
You may notice a "publish_stream" value which is important for us to post on the users wall. And we (just to show "extended profile permissions") will also ask for "home_town".
After this we will get an "access_token" which has to be passed to every API call we make.
Our “desktop application” login page
From what we have learned on the desktop authentication page we need the following URL to provide the user with the appropriate login page:
The base URL including our "Application ID"
https://graph.facebook.com/oauth/authorize?client_id=10181612...
next the "special redirect url"
&redirect_uri=http://www.facebook.com/connect/login_success.html
followed by the application type (determines how login works)
&type=user_agent
further the layout of the page (touch or wap) is OK for devices
&display=touch
and finally the privileges we need
&scope=publish_stream,user_hometownFacebook
The last step
After everything was ok we will be redirected to success page and retrieve the access token http://www.facebook.com/connect/login_success.html#access_token=...&expires_in=...
This kind of information (parameter) passing is known as "fragment".
So we have to take a different approach. This is a bit more complicated - but in general it is the same approach. We need to use the "Web application authentication" as described here: http://developers.facebook.com/docs/authentication/
It is almost the same as the "desktop authentication" except of 3 differences.
- We do not set the type to "user_agent". No problem at all - we just don't pass the type parameter
- We have to pass our "application secret". This is the real problem - normally we would never give a user access to this value. With web application it doesn't matter - the value never leaves our web server. But for client applications we have to "ship" this value inside our application.
- We have to make an extra call to get the "access token". We will do this in a very simple manner - no problem at all
Anyhow - this is a workaround so I hope with the wp7 RTM we'll no longer have a need for this.I also use the "special desktop authentication return URL" - it works also in our case.I'll include the "bug free" code in this sample - so if the bug is gone you'll already have to uncomment it.
Our start page
Silverlight differences to other platforms
Wp7 Silverlight is some kind of Silverlight 3.0 with extra controls and some (a lot?) missing features compared to the "full version".
We have special support for the application lifetime (state storage, extra events), access to hardware features (GPS, accelerator, Phone, ...) and the control set is different.
Important for our case - we have a "PhoneApplicationPage" and a navigation framework.
Both things are already included in the template - so we just have to use them. The most basic controls (Grid, Button, Text..., StackPanel, Image, ...) do also exist; so we don't take too much care about it and simply start coding our project.
The navigation framework
This framework provides us with the ability to navigate through our pages. The typical behavior is to open a page via a control (Button, List element ...) and leave it with the "Back Button" - a piece of hardware which every windows phone 7 device provides.
Screen orientation
In general there are three possible orientations (unfortunately "reverse portrait" which exists on the iPhone is missing) - portrait, landscape left and landscape right.
In our sample we'll only use landscape but the idea behind supporting different orientations is either a "fluid layout" or some "rearrange code".
Basic layout
"Metro Style applications" should have a common look and feel. On part of it is the information provided on top of a page. It consists of an application title and a page title. Both elements do already exist in the template so (if you want) simply change these values.
Notice there is a grid "LayoutRoot" which holds a StackPanel (the titles) and a second grid "ContentGrid" where we'll place our content.
Time to code
You can simply copy and paste the following XAML to your application. BUT - notice that you'll not have the used event handlers in your code behind (this results in XAML errors). Either ignore them (we'll add the handlers soon) or got into XAML - delete the name of the handler, type a blank and intellisense will ask you to create a handler for you.
01.
<
Grid
x:Name
=
"ContentGrid"
Grid.Row
=
"1"
>
02.
<
Grid.RowDefinitions
>
03.
<
RowDefinition
Height
=
"Auto"
/>
04.
<
RowDefinition
Height
=
"*"
/>
05.
<
RowDefinition
Height
=
"Auto"
/>
06.
</
Grid.RowDefinitions
>
07.
<
Grid
x:Name
=
"fbUserGrid"
Grid.Row
=
"0"
>
08.
<
Grid.ColumnDefinitions
>
09.
<
ColumnDefinition
Width
=
"105"
/>
10.
<
ColumnDefinition
Width
=
"*"
/>
11.
</
Grid.ColumnDefinitions
>
12.
<
Grid.RowDefinitions
>
13.
<
RowDefinition
Height
=
"Auto"
/>
14.
<
RowDefinition
Height
=
"60"
/>
15.
<
RowDefinition
Height
=
"Auto"
/>
16.
<
RowDefinition
Height
=
"Auto"
/>
17.
</
Grid.RowDefinitions
>
18.
<
TextBlock
Text
=
"User Data"
Grid.ColumnSpan
=
"2"
TextAlignment
=
"Center"
/>
19.
<
Image
Source
=
"{Binding Path=PictureLink}"
Grid.Row
=
"1"
Stretch
=
"None"
VerticalAlignment
=
"Center"
/>
20.
<
TextBlock
Text
=
"{Binding Path=Name}"
Grid.Row
=
"1"
Grid.Column
=
"1"
VerticalAlignment
=
"Center"
/>
21.
<
TextBlock
Text
=
"Gender"
Grid.Row
=
"2"
/>
22.
<
TextBlock
Text
=
"{Binding Path=Gender}"
Grid.Row
=
"2"
Grid.Column
=
"1"
/>
23.
<
TextBlock
Text
=
"Hometown"
Grid.Row
=
"3"
/>
24.
<
TextBlock
Text
=
"{Binding Path=HomeTown.Name}"
Grid.Row
=
"3"
Grid.Column
=
"1"
/>
25.
</
Grid
>
26.
<
StackPanel
Grid.Row
=
"2"
>
27.
<
Button
x:Name
=
"btnLogin"
Click
=
"btnLogin_Click"
Content
=
"Login to facebook"
/>
28.
<
Button
x:Name
=
"btnGetUserData"
Click
=
"btnGetUserData_Click"
Content
=
"Load User Data"
IsEnabled
=
"False"
/>
29.
<
Button
x:Name
=
"btnPostToWall"
Click
=
"btnPostToWall_Click"
Content
=
"Post to wall"
IsEnabled
=
"False"
/>
30.
<
Button
x:Name
=
"btnShowFriends"
Click
=
"btnShowFriends_Click"
Content
=
"Show Friends"
IsEnabled
=
"False"
/>
31.
<
TextBlock
x:Name
=
"txtStatus"
Text
=
"Login to enable facebook funtions"
Style
=
"{StaticResource PhoneTextNormalStyle}"
/>
32.
<
TextBlock
Height
=
"80"
TextWrapping
=
"Wrap"
x:Name
=
"txtError"
Text
=
"OK"
Style
=
"{StaticResource PhoneTextAccentStyle}"
/>
33.
</
StackPanel
>
34.
</
Grid
>
Figure 5: Layout provided by this xaml
The buttons "Load User Data", "Post to wall" and "Show friends" will be handled in part 2 of this article series. Here we'll focus on Login.
Navigate to the top of your XAML (place the cursor there) and via "Properties / Events" add a page loaded event handler.
So after all we have layout the loaded event handler and our button handlers. This is the code:
01.
private
void
PhoneApplicationPage_Loaded(
object
sender, RoutedEventArgs e) {
02.
}
03.
private
void
btnLogin_Click(
object
sender, RoutedEventArgs e) {
04.
}
05.
private
void
btnGetUserData_Click(
object
sender, RoutedEventArgs e) {
06.
}
07.
private
void
btnPostToWall_Click(
object
sender, RoutedEventArgs e) {
08.
}
09.
private
void
btnShowFriends_Click(
object
sender, RoutedEventArgs e) {
10.
}
Our approach is to load the "Login page" which shall (after a successful login) provide us with the "access_token" we need for further calls to the Facebook API.
Understanding navigation
Although you can provide your own navigation framework we'll use the built in one. This works by removing the current page and loading the page we are navigating to.
Or in other words - every page is created from scratch when we navigate to it. This is also true for the calling page. So navigating to PGLogin.xaml and coming back to the main page after login means:
- MasterPage is destroyed
- PGLogin is created (
loaded
event) - PGLogin is destroyed
- MasterPage is created (
loaded
event)
The problem now - how to pass parameters to / from PGLogin. The navigation framework works pretty much like HTML navigation, so we can pass "Query strings" and retrieve them in a special event: OnNavigatedTo
Controls are not loaded in OnNavigatedToBe aware that controls are not fully loaded in this handler. So for an example a "webBrowser.Navigate" will fail with an exception "must be fully loaded before you navigate..."
In our case we'll take a different approach - we'll keep the shared information (access_token) in a global static variable. The best place for this is App.xaml.cs (the code behind App.xaml).
Select App.xaml in the Solution Explorer, right click it an choose View Code (F7). Add the following code;
1.
#region AccessToken
2.
private
static
string
m_strAccessToken;
3.
public
static
string
AccessToken {
4.
get
{
return
m_strAccessToken; }
5.
set
{ m_strAccessToken = value; }
6.
}
7.
#endregion
Save and close the file.
Next let us navigate to our Login page - I assume you named it (like shown above) PGLogin.xaml.In MainPage.xaml.cs change the login button handler to:
1.
private
void
btnLogin_Click(
object
sender, RoutedEventArgs e) {
2.
NavigationService.Navigate(
new
Uri(
"/PGLogin.xaml"
, UriKind.Relative));
3.
}
What are we doing here? We use the Navigation service and tell it to navigate to our login page. We don't care about coming back - you remember - this is done via the "Back Button" on our windows phone 7 device.
Before we first run the application we will simulate a login. This has two reasons - first it is time to see something going on - second this will show how we can intercept the "Back Button navigation".The reason for doing this could (for an example) be a data entry form where we detect changed data, and ask if the user wants to save the changed data before he leaves the page.
Open PGLogin.xaml - place the cursor on top of the xaml and via "Properties / Events" add a handler for the BackKeyPressEvent. Add the following code to the handler.
1.
private
void
PhoneApplicationPage_BackKeyPress(
object
sender, System.ComponentModel.CancelEventArgs e) {
2.
App.AccessToken =
"TOKEN_SET"
;
3.
}
The only thing left is some kind of "Logged in signal" on our main page.Did you notice the text (second line from the bottom) on our Main page? "Login to enable..."
So let's enable the buttons. Change the page loaded event handler to:
1.
private
void
PhoneApplicationPage_Loaded(
object
sender, RoutedEventArgs e) {
2.
bool
bWeAreLoggedIn = !
string
.IsNullOrEmpty(App.AccessToken);
3.
btnLogin.IsEnabled=!bWeAreLoggedIn;
4.
btnGetUserData.IsEnabled = bWeAreLoggedIn;
5.
btnPostToWall.IsEnabled = bWeAreLoggedIn;
6.
btnShowFriends.IsEnabled = bWeAreLoggedIn;
7.
txtStatus.Text=bWeAreLoggedIn ?
"Use the above buttons to access facebook"
:
"Login to enable facebook funtions"
;
8.
}
Run your application and check if the things work like expected. They should, if you didn't make a copy and paste error.
The Login Page
The best things come in threes
As on the main page we'll add 2 informational controls on the bottom of the page. And as real content we need a WebBrowser Control. Replace you PGLogin Content grid with the following markup:
01.
<Grid x:Name=
"ContentGrid"
Grid.Row=
"1"
>
02.
<Grid.RowDefinitions>
03.
<RowDefinition Height=
"*"
/>
04.
<RowDefinition Height=
"Auto"
/>
05.
</Grid.RowDefinitions>
06.
<phone:WebBrowser x:Name=
"wbLogin"
IsScriptEnabled=
"True"
/>
07.
<StackPanel Grid.Row=
"1"
>
08.
<TextBlock x:Name=
"txtStatus"
Text=
"Loading login page"
Style=
"{StaticResource PhoneTextNormalStyle}"
/>
09.
<TextBlock Height=
"80"
TextWrapping=
"Wrap"
x:Name=
"txtError"
Text=
"OK"
Style=
"{StaticResource PhoneTextAccentStyle}"
/>
10.
</StackPanel>
11.
</Grid>
Remove the BackKeyPress handler from PGLogin.xamlThis is important else it would override our access_token.
Notice that I set IsScriptEnabled
to true
on the WebBrowser control. The Facebook pages use a lot of scripts and by default scripting is disabled. So in order to have functional pages we need to enable it.
URLs URIs and more
The whole Facebook API access is done via "web links". Silverlight wants to have URIs instead of URLs (strings). And we'll need a lot of them. So I decided to create a helper class which provides those URIs.Since we'll need other helper classes for part 2 of this article series I first add a folder to the solution, so the things are better organized. Right click you project in Solution Explorer and add a folder call "HelperClasses".
Next right click this folder, choose "Add / Class" and add FBUris to the project. The project should now look like this:
Figure 6: Project tree
Our FBUris class will provide us with well formatted URIs. I decided to make the class static.
01.
public
static
class
FBUris {
02.
#region AppID
03.
private
static
string
m_strAppID =
"101816123213455"
;
04.
#endregion
05.
#region AppSecret - only needed because of the fragment bug
06.
private
static
string
m_strAppSecret =
"a96c7b28c49664b12a5fe8a1555388b3"
;
07.
#endregion
08.
09.
10.
11.
&redirect_uri=http:
12.
private
static
string
m_strLoginURL = "https:
13.
14.
&redirect_uri=http:
15.
private
static
string
m_strGetAccessTokenURL = "https:
16.
17.
&redirect_uri=http:
18.
public
static
Uri GetLoginUri() {
19.
return
(
new
Uri(
string
.Format(m_strLoginURL, m_strAppID), UriKind.Absolute));
20.
}
21.
public
static
Uri GetTokenLoadUri(
string
strCode) {
22.
return
(
new
Uri(
string
.Format(m_strGetAccessTokenURL, m_strAppID, m_strAppSecret, strCode), UriKind.Absolute));
23.
}
24.
}
Please ensure that you use your own application Id and application secretThe used values are no longer valid - I deleted the application.
Don't take too much care about the TokenLoadUri
and GetAccessTokenURL
- I told you above, that we need a third step to retrieve the access_token
since we have to use "web authenticate". I'll explain this later when it takes place in our code. Notice also that I provided the "not bugged" URL as comment.
The rest is pretty simple - the changing parts of our URLs are marked as "string format placeholders" {x} so that we can pass these values as parameters.
Now that we have got our "navigation addresses" (URIs) easily use them in our code. The first thing we want to do is to load the Facebook login page into our WebBrowser control. A good place to do this is the PageLoaded event handler. Add a Loaded event handler to PGLogin and change it to:
1.
private
void
PhoneApplicationPage_Loaded(
object
sender, RoutedEventArgs e) {
2.
wbLogin.Navigate(FBUris.GetLoginUri());
3.
}
When you run the application our login page should look like this:
Figure 7: Empty login form
After entering your credentials, click login and you'll be redirected to the "application permissions" page:
Figure 8: Application asking for permissions
Scroll down (drag content) and you'll find the "allow / Don't allow" buttons. Don't press a button now - just close your application.
You see, it was pretty easy to get this going - now comes the (a bit) harder part. In a "bug free" situation the things are easier - but we have to do a bit more.
Reading the Facebook documentation (in this case for Web-Login) we learn that
If the user authorizes your application, we redirect the user back to the redirect URI you specified with a verification string in the argument code, which can be exchanged for an oauth access token.Exchange it for an access token by fetching https://graph.facebook.com/oauth/access_token.Pass the exact same redirect_uri as in the previous step:…
So (for a web application) this means to place the "redirect URL" to a page at your site where you check and use the parameter sent to the page. But we have no web server - we have a windows phone browser control.
No problem at all - remember we set a (stolen from desktop authentication) special URL. All we have to check is when the user (the browser via redirections) reaches this page. Then we should find the "code" in the URL of that page. Our "special page" was http://www.facebook.com/connect/login_success.html and it should have a "code" parameter set. So the final URL would look something like: http://www.facebook.com/connect/login_success.html?code=...
Fortunately there is an event when the WebBrowser control loaded a page. Goto XAML of PGLogin, locate the WebBrowser Control and inside the tag type LoadCompleted
= - which brings up intellisense and allows you to add an event handler. The LoadCompleted
event fires when the WebBrowser finished loading a page.
This event handler has a parameter of the type "System.Windows.Navigation.NavigationEventArgs". This class has a property called Uri - which represents the address (and parameters) of the page the browser loaded.
To get the "bugged" version going we simply check if we reach an address that starts with the expected value and behind this the "code" can be found. So the handler should look like this:
1.
private
void
wbLogin_LoadCompleted(
object
sender, System.Windows.Navigation.NavigationEventArgs e) {
2.
string
strLoweredAddress = e.Uri.OriginalString.ToLower();
4.
txtStatus.Text =
"We got the code"
;
5.
txtError.Text = e.Uri.OriginalString.Substring(56);
6.
return
;
7.
}
8.
}
For the first step we just display a message that we got the code - and in the last line (blue one) of our form we display the code itself. There is no need to lower the URL - but I prefer this. Maybe Facebook decides to forward to the same URL with different casing...
Web authentication get’s a code and has to change this to an access tokenAs I told above there is an extra step to obtain an access_token needed with workaround. This is how it works.
The code is no help at all for us. For web applications to retrieve the (needed) access_token it requires an extra step which must provide the applications secret and the (just received) code.
In the class FBUris we already have a method which builds this request for us. The URL looks like this:
First again a URL where we pass our application ID
https://graph.facebook.com/oauth/access_token?client_id={0}
then we must provide the exactly same URL as in the call before as redirect
&redirect_uri=http://www.facebook.com/connect/login_success.html
further we must provide our application secret
&client_secret={1}
the last parameter is the code we got from the previous call
&code={2}
Again we change our LoadCompleted
handler now to:
1.
private
void
wbLogin_LoadCompleted(
object
sender, System.Windows.Navigation.NavigationEventArgs e) {
2.
string
strLoweredAddress = e.Uri.OriginalString.ToLower();
4.
txtStatus.Text =
"Trying to retrieve access token"
;
5.
wbLogin.Navigate(FBUris.GetTokenLoadUri(e.Uri.OriginalString.Substring(56)));
6.
return
;
7.
}
8.
}
This means we load a different page and this will also redirect to http://.../connect/login_success.htm
Instead of sending the access_token
as a parameter this value is passed as content of the page. So we could also use a web request to do this. But since the browser is already there we'll use this guy to do the job.
So again we check for this page - but this time without the parameter ?code=…
When we find it we access the page content and parse it for the access_token. The content of the page looks something like <HTML><BODY><PRE>access_token=….</PRE></BODY…
01.
private
void
wbLogin_LoadCompleted(
object
sender, System.Windows.Navigation.NavigationEventArgs e) {
02.
string
strLoweredAddress = e.Uri.OriginalString.ToLower();
04.
txtStatus.Text =
"Trying to retrieve access token"
;
05.
wbLogin.Navigate(FBUris.GetTokenLoadUri(e.Uri.OriginalString.Substring(56)));
06.
return
;
07.
}
08.
string
strTest = wbLogin.SaveToString();
09.
if
(strTest.Contains(
"access_token"
)) {
10.
int
nPos = strTest.IndexOf(
"access_token"
);
11.
string
strPart = strTest.Substring(nPos + 13);
12.
nPos = strPart.IndexOf(
"</PRE>"
);
13.
strPart = strPart.Substring(0, nPos);
14.
App.AccessToken = strPart;
15.
16.
17.
txtStatus.Text =
"Authenticated - use back to see results"
;
18.
txtError.Text =
"OK"
;
19.
return
;
20.
}
21.
}
The final code has a commented option. In fact it could be a good idea to just bring the user away from this page to where he came from (NavigationServices.GoBack
()). So PGLogin would act like a login dialog which closes when the login is done.
Let's make a change in the MainPage Loaded
event handler. It will display the access_token instead of "OK".
01.
private
void
PhoneApplicationPage_Loaded(
object
sender, RoutedEventArgs e) {
02.
bool
bWeAreLoggedIn = !
string
.IsNullOrEmpty(App.AccessToken);
03.
btnLogin.IsEnabled = !bWeAreLoggedIn;
04.
btnGetUserData.IsEnabled = bWeAreLoggedIn;
05.
btnPostToWall.IsEnabled = bWeAreLoggedIn;
06.
btnShowFriends.IsEnabled = bWeAreLoggedIn;
07.
txtStatus.Text = bWeAreLoggedIn ?
"Use the above buttons to access facebook"
:
"Login to enable facebook funtions"
;
08.
txtError.Text = bWeAreLoggedIn ? App.AccessToken :
"OK"
;
09.
}
This ends part one of the article series. In part two we will retrieve and send data using the Facebook API.
Resources