Published: 13 Dec 2010
By: Manfred Pohler
Download Sample Code

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 available – for downloads take a look at the links at the end of this document.

Contents [hide]

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
  • Part3
  • Introduction

    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 available – for downloads take a look at the links at the end of this document.

    In this part I use two third party tools.

    This is part three of an article series – if you didn't check part one and two you should do it know since this article assumes that you have the information from part one and two.

    The first two parts had the title wFace. But since facebook got a patent on "Face" (http://techcrunch.com/2010/11/23/patent-office-agrees-to-facebooks-face-trademark/) I decided to user the German word Gesicht (http://www.dict.cc/?s=gesicht)

    What we’ve done so far

    With part 2 of the sample we are able to:

    • Login to facebook authenticating our application
    • Post to OUR OWN wall
    • Read our user data
    • List our friends

    Requests from Comments

    • Can we get wall data
    • We'll do this here
    • Logout. We'll do this here
    • Login bug. Old bug is fixed – we have a new one with workaround

    Covered in this part

    This part is pretty long (from topics) but it uses technologies explained in part 1 and part 2 so we can focus on what we want to do.

    • Login with better handling. Also fixing a new bug
    • Logout – on an extra form
    • Post to our wall and to friends wall. To make it simple we post a "multiple choice message"
    • Read our wall
    • Read the wall of our friends
    • Query specific messages using FBQ. We do this for "application messages" – but it can be used for every kind of available data.
    • Caching loaded information. We do this just in 2 places – but it can be easily enhanced for other sections. Better performance – and useful for the next point.
    • Resuming application
    • Background loading
    • Enhanced data-binding
    • A cool looking application icon, tile image and a splash-screen. Check this out!!

    The better Login

    Login as we know from part tow can't be done like it should since the WP7 web browser control "eats" the uri.Fragment. I explained this in part 1. And we built a workaround for this.

    No we have a new bug (discussed in the facebook forums and filed as bug) which brings up an error page "Something went wrong" after a successful login. Fortunately a redirect back to the login pages solves the problem. The login detects that we are already logged in and redirects again – this time to the correct page.

    The page does no longer wait for a back – instead after user cancel or successful login it displays a "dialog" and redirects to the main page. This is how it looks:

    Figure 1: Successful login

    Successful login

    The code for this looks like: (PGLogin.xaml.cs - wbLogin_LoadCompleted handler)

    The last condition (StartsWith(“https...)) is about the new error. It detects the error page and redirects to the login again.

    Also new is the part (top of the code) where we extract the access token from the response. Before we displayed a text telling the user that we are logged in an he should press the back button.

    Now we do something very different. First we make a call to App.CurUserHolder.LoadUserData(). I'll discuss this later. Next we set the status text and then wndLoginConfirmed.IsOpen=true;

    This code opens the "dialog" with the OK button. The handler in the OK button finally does hide the dialog again and then it goes back to the main page.

    Now what is this dialog? It is part of the Telerik WP7 controls and the markup looks like this:

    It is nothing more than a container where the content is what we want to see in our dialog. There is no "code magic" – this is everything you need to place on your form. And later you call wndXXX.IsOpen=true; to display this dialog. To close it simply set IsOpen to false. Further (I use this in PGLogin.XAML) you can define animations for open and close to give the "dialog" a cool appearance.

    That's our new login – but stop – there was this App.CurUserHolder.LoadUserData().

    NOTE

    For the following code you should be familiar with INotifyPropertyChanged since it is the glue between our classes (members) and data-binding on the forms. Glue means that the UI needs this to update if needed.

    http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx

    The UserLoader

    In part two we loaded our user data by pressing a button on the mainform. The exact (almost) same code is now in a class called "UserLoader". The (big) difference – before we set the values of our controls directly in the main form after we retrieved the data. This time we simply set properties in our UserLoader class.

    The important part of the data received handler looks like this:

    It's almost the same as before – but before we set the datacontext of the main form to our new user. Now we can't do this since we are no longer in our main form. As you may see later it is not even sure if the main form is present when we get the user data. And before I show the magic behind lets also check the UpdateStatus. This was UpdateUIStatus before and set 2 TextBlock.Text values. Now it simply sets to string properties in my class – LastError and LastMessage.

    And notice the "StoreDBUser" which is done after user data was successfully retrieved. I'll discuss this later for now just let me say – we store our access token and the user data in a database for later use.

    As you can see we have some simple properties like LastError and LastMessage and we further have some Boolean or Visibility members which just check if a user is present. These are use to hide / enable controls.

    And the property Users also fires OnPropertyChanged events for those Boolean / Visibility properties.

    Also notice the DBHelpers.DeleteDBUser() call. I'll explain this a bit later. But expect that it does what it says – it deletes token and user data from the database – if we no longer have a valid user (User==null). It is the opposite from what we did above when we had successful user data retrieval and called StoreDBUser.

    Is this all? Yes – the only difference to part 2 is that FBUser now also implements INotifyPropertyChanged.

    Let's resume what we have so far:

    • Login stores the AccessToken in App.AccessToken (like before)
    • We have an App.UserHolder (static member UserLoader)
    • Login calls App.UserHolder.LoadUserData() before it closes
    • The UserLoader retrieves the user data (before the main page did this)
    • UserLoader and FBUser implement INotifyPropertyChanged
    • We have some mystical DBHelper calls – DeleteDBUser / StoreDBUser

    The new MainPage

    Figure 2: MainPage logged in

    MainPage logged in

    Figure 3: Main page logged out

    Main page logged out

    First let me tell you that code behind does (almost) nothing!

    That's all the rest are button handlers which do nothing more than call pages with parameters. All the enabling and disabling of the buttons as well as the display of messages or user data is done by databinding.

    So let's have a look at the XAML:

    By the way – the whole stuff is in a Grid call ContentGrid where the UserLoader object is bound to. Therefore we can enable / disable our buttons with the UserEnable and LoginEnabled properties of the (above described) UserLoader class.

    Also notice that on top of the XAML is a border – it's visibility is set via "UserVisible". And (second line) inside is a Grid which is bound to User. Inside this grid finally we have our (know from part 2) FBUser Members. Magic? No, pretty simple!

    We have on top bound a class UserLoader. This has a member (type FBUser) called User. And so the inner grid binds to DataContext.User – or in other words UserLodader.User. Next step is that the fist texblock inside this grid binds to Name which means – UserLoader.User.Name.

    Since the classes implement INotifyPropertyChanged we have nothing to do than to set the properties which automatically updates the UI. This has a positive side effect – we have no need to take care on which thread the data changes (no need for Dispatcher calls).

    To finalize our main forms explanation I'll talk a view words about the button handlers. Login is known from part 2 – it opens a new page. Logout does exactly the same. And Show Friends is also known from part 2 as a simple page opener.

    The rest (My feeds, My Wall, Application Feeds) all call the same form – Feeds.xaml. To enable this from to distinguish between the different calls we pass parameters. It looks like this:

    We always pass the ID – either as a normal user ID or as "me" / "app". Feeds.xaml checks this and decides what to request from facebook.

    If you compare part 2 with this part you should see a noticeable code reduction. And finally the code we had to add was more or less "dumb interface implementation" (INotifyPropertyChanged). But exactly that is one of the strengths of Silverlight / WPF. As soon as we are able to use data binding we save a lot of code and code becomes easier.

    Now there are three interesting parts to explain. First the "database mystery" which implies the second thing "WP7 Resume handling" and things like this. Last not least the facebook things.

    I guess you got used to "database is a miracle" so let me go on with facebook and fulfill one of the comment requests – the logout functionality.

    Logout is crazy – facebook won’t let you go

    There are lots of threads out there about difficulties with logout from facebook. And I got a solution – at least on the iPhone. But this solution implies access to the browsers cookies. In WP7 we do not have access to these cookies. Let me explain. The normal logout page logs out the application but not the user. So further call to the login page detects "same user again – application already accepted – token still valid" and it simply authenticates without any chance to interrupt this process.

    With the iPhone the things are simple – KillCookies – call Logout – redirect to Login. The login page doesn't find any cookies and so it calls for credentials again.

    A lot of trial and error (after useless googling) brought me to an idea. Why not redirect to a different page. But let me explain this step by step.

    The "official" way to log out an application is to call the following URL:

    http://facebook.com/logout.php?confirm=1&app_key={0}&session_key={1}&next=LOGIN_URL

    This – after killing the cookies brings the login form again – and a new user can login. But as explained it doesn't work due to cookies (which is a problem with the facebook api – it should kill those cookies).It could also be a page hosted by you if you just want to logout and show a nice message.

    Anyhow redirect to the login page is a no go solution. And any other page is also useless because the next time we hit the login page will result in an "auto login" due to the cookies.

    I messed around with a lot of pages on facebook – and finally ended with the homepage. The reason – this page has a logout button. And here I saw a light at the end of the tunnel.

    I changed the URL to:

    …/logout.php?confirm=1&app_key={0}&session_key={1}&next=http://facebook.com/home.php

    And in the browser I see my homepage – still logged in. That is (of course) normal, since the logout logs out the application – not the user itself. Manually clicking the logout button works. After this I can visit the login page and (without killing any cookies) it asks me for credentials.

    So now simply do this in code – easier said than done. The problem a simple redirect to ...facebook.com/logout.php doesn't work. Now I checked the source of the page looking for the logout button. This is a link which redirects to /Logout.php including a lot of parameters. I parsed the page content and extracted this link – didn't work either.

    Next I recognized that I land on different URLs – sometimes on www.facebook.com – sometimes on m.fac...Unique to this URLs was a parameter fbb=xxxnnnx. The approach was to pass the correct address (www / m ) and the correct parameters. So the simplest solution was to replace home with logout to change the URL.

    And that is the final works passing the fbb parameter and using the same host (www / m / ???) is enough to make it working. So first I navigate to logout.php passing app_key and session_key which brings me to home.php with the application loged out. From there I have to redirect to logout.php again (on the correct host) passing the fbb parameter. The code now looks like this

    The code is straight forward. Except an extra check (m_bDidAppLogout) I do nothing special. By the way I again use a Telerik window to display a dialog.

    I searched for hours – but there was no hint on the web going further than killing cookies, use the JS api and other crazy solutions. And when I say hours I mean a lot of hours. My search started when I made my RPS iPhone application – and continued when my solution to kill cookies was not useable with WP7.

    So I'm really happy that I could work this out! And I would say – comment request fulfilled.

    Notice – App.CurUserHolder.User is set to null here. This has two outcomes – the main page is updated and the DBHelpers.DeleteDBUser gets called.

    NOTE

    We have to pass the session ID not the access token – check FBUris (bottom) to see how to extract this value.

    Feed (the monkeys)

    Also a comment request was to read a users wall. We have to distinguish between two things: OUR wall and the one of our friends. Our own wall is a thing we normally don't see. You can see it if you click on profile when you are at you facebook homepage. And that is what our friends see. Or opposite that is what we can retrieve from our friends via api. So in fact our wall and the wall of our friends is the same.

    But with our account we can also load our feeds (what we see on our facebook homepage).

    The difference is the URL.

    For the wall we call /USER_ID/feed... and for the news (our homepage) we call /me/home...

    A post has tons of information, ever "like", "comment", "attachment" (video, image, ...) and so forth produce a lot of data. You can simply change the URL and remove the fields=... to what's out there. We (for simplicity) reduce the output to id, from, to, caption, description, attribution, message.

    Have a look at part 2 where we post our message. There we use caption, description and message. What are the other fields? These are id==id of the message, from==id of the one who posted, to==id of the one to at who's wall the post was made. Last not least Attribution is the name of the application which was used to post. This last value is used to produce the small info on the bottom of a post like "..via Netvibes.com".

    By the way – from and two are facebook user elements with name, id... Or to be more precise "from" is a facebook user and "to" is an array of facebook users.

    What we get back from these calls is an array of post. To simplify de-serialization I built the following class:

    And the elements of the array look like this (some simple properties left out):

    Interesting here is the start where I declare a class which get's a member called data which holds an array of FBUser elements. This is used for the "to" result. And to make the display easy I built an accessor for the first element of this array. Of course you can change the display and use an items control (listbox or so) to show all "to" members.

    You may have notice that I declared a different class in the list (array of FQStreamResult). This class is derived from FBFeed and needed for facebook queries (see later). Anyhow I would say (except for the trick with the "to" array) FBFeed is a simple class. There is no need for INotifyPropertyChanged since an element never changes its properties. Instead we always get a new (list of) element(s) when we fetch a feed.

    Let's have a look at the feed display:

    It is nothing more than a list box with a 2 column grid which displays the property name and its content. Here is how the page finally looks:

    Figure 4: Feeds with messages

    Feeds with messages

    You may wonder what the "Post to user's wall" does. It is a kind of "list selector" which allows us to generate posts. This is again a solved request from a comment.

    But for now let's focus on getting feeds. No matter what we request (friends wall, our wall, our feed, application feeds – we always use this form. And as I wrote in the topic about the main page – the difference is made by parameters.

    You have seen above how to pass parameters to a page.

    /Feeds.xaml?id={0}&name={1}&piclink={2}

    This is similar to passing parameters to a web page. And the retrieval is also similar. To handle this we overload the OnNavigatedTo function in our destination (Feeds here) page. It looks like this:

    As you can see at the end of the code is the OnNavigatedTo handler. Here we extract the parameters using the NavigationContext.QueryString.TryGetValue function. We take car about the ID parameter the rest is not important. PicLink is not used (would us also to display the users picture) and name is used for the blue line on top of the page.

    The main work is done in the ID property handler (top of the code). There we check what it is and select the fitting URL. Further we disable the "Post to user's wall" if we display application messages. It is possible to post to an application page – but this is not covered here.

    After assigning the values we start to download the feeds. This looks so:

    First we make a call to the already (in ID property set handler) set URI. When we get a message we have to distinguish between "normal messages" or "application messages". The reason – these messages are fetched via the facebook query api which has a different result. And it has (if empty) also a "not fitting" result.

    Normally as it returns an array of elements it should act like the other array returning functions. They return data:{[elements]} – here we simply get [elements]. Or (strange I'd say) {} in the case of nothing is found.

    We handle this by correcting the returned string to be consistent with the other calls.

    Also notice that we deserialize everything as FBFeedlist although a query result is different. But inheritance makes the serializer work correct in this case. Now let's talk about this query thing.

    FQL – ask facebook

    In general FQL is a kind of SQL like language which allows us to query data from facebook. In our case we concentrate on the "table" stream. http://developers.facebook.com/docs/reference/fql/stream.

    NOTE

    Important: every query must contain at least on "indexable" field. This means that you can (for an example) not query for posts made by your application with the condition WHERE app_id=1233333

    Since we can (in any case) only read posts from our friends or our self we always include the following:

    AND (source_id IN (SELECT uid2 FROM friend WHERE uid1 = me()) OR source_id=me())

    This is a "sub query" which says "the author" is one of our frieds or we made the post.

    In our simple sample case we are looking for posts made by our application. To send a query we have to use a special URL which looks like this:

    https://api.facebook.com/method/fql.query?access_token={0}&format=JSON&query={1}

    This is a URL similar to those we use till now. Important is only to pass the format=JSON parameter. Otherwise we would get XML. And the last parameter is the query itself. Here our query:

    You already know the last line is a "we have to use sub query". Line two is the table (stream which hold feeds) and the app_id for what we are searching. Finally the first line holds the fields (there are plenty more) which we need. Unfortunately the format is a bit different than the one of a normal feed request. But last not least we can find almost the same data (except for from / to where we only get the ID – which we could use to query for the rest). Here is the class for such a result:

    Again we need a "inner holder" since a part (Name, Caption, Description) of the information about the post is in the "attachment" field. Next we have (on top) fields with different names (FBFeed.ID == post_id).

    Finally we build our FBUser Elements (from, to) just providing the ID.

    FQL is a bit tricky but on the other hand is it a tool which enables you to query almost everything. Great for writing bots I'd say :) And about bots – and mass query – the problem and indexed field must be in the query and this can either be a sub-query result or a fixed value. A query (good idea) with … source_id>0 would fail.

    Two things you should consider when you use FQL. First – you only get information you are allowed to retrieve. For an example a query to the user table asking for hometown_location will fail when you app hasn't asked for the privilege to retrieve the hometown. Second – some values may be localized – so check out the locale parameter like we do with our "retrieve user data" URL (see FBUris.cs).

    Post (make the writing on the wall)

    A further request in a comment was about posting to a wall. No, sorry this was not a comment; it was a message to me about "you showed how to post on your wall – how about other walls".

    Anyhow – this time we make it a bit different as in part 2 where we had a form with a lot of fields to fill out. An app typically has its own format of post – just think about farm...

    So I tried to mimic such an approach – the user does (chooses) something and the app builds a post out of it.Our app will be something like a "how do I feel dispatcher". For this I created a class called Mood:

    This class has a "short text" a complete text and a picture. Further is has an ImagePath property which combines the image name with a path relative to our application root. The GetMoods static function provides a list of "predefined moods".

    In the application we have a folder called images where I placed the images. Important – set build action to "Content" and "Copy to Output Directory" to "If newer". This includes the files in your deployment.

    Our (known from part 2) get's a new constructor which has such a Mood as parameter:

    Of course (in this case) the images must have the same name on the site you are addressing with the "ThePictureLink". Since facebook load the images on demand you can only provide a link and not upload an image with a post. Posting stays also the same – but how do we generate a post?

    On the feed page you have this "button" on the bottom of the form. But it isn't a button – it is something like a "combo box" – and follows the metro design guidelines. Instead of inventing the wheel again, I used another control from the Telerik WP7 controls – the RadPickerBox.

    Looks strange? No it's simple. Inside the control we have a "PopupContent" where we can place whatever we want. In this case a list box which will be bound to the result of Mood.GetMoods. There we bind an image (that's why we needed the PictureLink property in Mood), and the ShortText.

    Above the PopupContent we see a "ApplicationBarInfo" section. Think about it as a normal application bar which is displayed when needed. Finally the ApplicationBar has a handler for ButtonClicked. This is where we decide what to do. And after this since the "API" is consistent (as with all Telerik controls – that's why I love them beside other reasons of course) so we can do the same as we did with the window (our dialogs) before – IsPopupOpen=false to close the selector window.

    There is no need to open the selector through code (although we could if we have a need) – it opens when we click at the "button". In the ButtonClicked Handler we check what happened (Post – cancel) and if we should post we send our request to facebook. Here is the code:

    If we get a post (not cancel) we get the selected element from the list box – use it as parameter to construct a FBWallPost and send it (like we did in part 2). At the end we set pbMoods.IsPopupOpen=false to close the selector. Someone may have noticed that there is a wndDoPost.IsOpen=true. There are no marks for guessing – again we open a "dialog" this time to show a progress indicator because posting take time.

    Let's see how this looks:

    Figure 5: Feed page when posting

    Feed page when posting

    The first image shows the selection (with application bar) and in the second one you see the "waiting dialog".

    Now I wrote above that we can post to a users (not only our) wall. On the main page we have no button for this. Of course not – because for this we would first have to select to whom we want to post. Therefore I change the Friends page. Now we have (on the existing list box from part 2) a selected event handler:

    Almost the same as on the main page – here we simply use the selected item to fill our parameters. Which brings me to the friend page – and there a lot of changes happened.

    Friends (new friends)

    First let's have a look at the friend page – it's a bit different than in part 2 of this series.

    Figure 7: The new Friend Page

    The new Friend Page

    So what's new? Friends are sorted by Name. Then we have a "refresh button" in the application bar. And this has something to do with the strange status text "Web: 1,153.00 DB: 310.00". This means loading from the web: 1.153 seconds; clearing friends in DB + storing friends in DB + reloading friends from DB: 0.31 seconds.

    DB? The mystical database again. Yes it is. But let's first check the whole code for the new friends page:

    The new solution has significant lesser code. As with the main page I moved the loader to a different class. Here I do not much – once I clear my in memory friends list (to show DB performance) and then I unselect the list if something is selected. This enables to go to the same feeds (from the same friend) again. Else (if it stays selected) no "SelectionChanged" would fire. A simple approach – but I've already seen apps where you can't "reselect" an item. Instead you have to select a different item first... Here means selection loading the feeds – so I did my best (simplest) to avoid this.

    But now (after a long time waiting) I introduce the database.

    The Need for a Database (we all have our crosses to bear)

    The subtitle with the crosses has two meanings. First it is hard to develop on a platform without a database. And second cross development in the meaning of cross-platform development.

    To be serious – this application (at least for me) could be done without a database. I have very little friends on facebook (nobody loves me) and the data behind the friends is pretty small (name, id in this sample). But maybe one of my readers has more friends, and maybe someone extends this example to store query results also (by the way a recommendation from facebook when using FQL).

    And last not least this is an example not a real world application. But let's talk about the need for a database.

    I read somewhere (at MS.xxx and other places) that Windows Phone 7 is intended to be a business device.

    Let's define a business application. Business almost always means data. This can be schedules, a list of customers, a product / price list, activity reports... Next these apps need to communicate: send back information to the office, retrieve / update information from the office.

    How is this supported in WP7?

    • Web Services - Online
    • Networking functions (up- / downloads) - Online
    • Azure (Cloud) access – Online
    • Push notifications - Online
    • Local storage the only one working offline

    Can you imagine the situation where you can't inform your customer about a products because you have no internet connection?

    The only built in way to overcome this is using object serialization. This will work with a certain amount of data. With more data we must expect performance and size (space on the device) problems. Further it can result in pretty complex code (joins, projections,...). And last not least the risk for total data loss is big – if the device (our app) stops (sent to background / crash) to work while storing a large set of objects the resulting file will be damaged an so no longer readable. Data is gone!

    If we have a look at the market then we'll find two big players – Apple and Android. Both have a database included; the one who wants to compete with them shines with "the absence of a database". By the way – I really thought this is a kind of joke and Microsoft will finally ship WP7 with a database – I was wrong!

    From my point of view an application should run on as many platforms as possible. This also includes the desktop and the web (Silverlight, ASP.NET). If Microsoft would have included "SQL Server compact" we could at least support Web, Desktop and WP7. Of course iPhone and Android are not on the list.

    My idea is to build a database on the desktop, test it with desktop apps (WPF, WinForms) where I have a broad range of tools and possibilities. After the database (including the Model or Data Access Layer classes) is finished and well tested I want to use it in my mobile applications as it is.

    Finally I found a solution which covers all of my needs. Except one – it is not free; at least if you want to use it commercial. But on the other hand it is not that expensive and if I sell my application(s) ROI should occur pretty fast. It is the same as with MonoTouch (.NET for iPhones) or MonoDroid (.NET for Android). With a little difference – those tools cost money, even if you only build free applications or just want to play with it.

    So what we've got now is a database for WP7 (http://effiproz.pp-p.net). It also runs on the desktop, so we can use VS to design our object and build nice desktop apps to test it.

    Last not least – back to the cross – the result will also run on the iPhone and when MonoDroid is released on Android devices. Now let's have a look how and why I decided to use a database in this sample.

    Resume - Restart

    First let us discuss a bit about "stay logged on". I'm sure you know these facebook, twitter, MSN... applications which allow you to stay online. When you start the application again you are automatically logged on. This may be a security risk – but who cares; it's my phone and I use this with most applications.

    There is another reason to store information (at least a while) which is interruption. This occurs (on the iPhone also) when the user leaves your app for a short time; for an example to answer a phone call or to read an email which just came in (Push notification).

    Microsoft (WP7 Design guidelines) tells us that we should do like we have been running in the background (their way to implement multitasking). What exactly happens when the user clicks on an email notification?

    Our app is terminated (a special function is called before) and the email client opens. When the user then presses "back" our application is "resumed". Since we use the integrated navigation framework we are brought back to where we have been. Check this out with part 2 of this series.

    To simulate an interrupt (works even with debug!) press the home button and after it the back button. If you do this (for an example) on the Friends page you will find yourself at the Friends page again – having problems (

    We are logged out (part 2 doesn't store the access_token) and can no longer access the facebook api. We don't even check for this! You may remember that the button which opens this page is locked if we are not logged on so we had no reason to check it. But the "automatic resume" doesn't care about this.

    The sequence for the friend page in "normal mode" is this:

    • Main page constructed
    • Main page loaded
    • Click on "Friends button"
    • Friends page constructed
    • Friends page loaded
    • Back is available – brings us to Main page if we click we get
    • Main Page loaded (is in memory no need to construct again

    On a resume the sequence is:

    • Friends page constructed
    • Friends page loaded
    • Back is available (stack is restored) – if we click we get
    • Main page constructed (never loaded before!)
    • Main page loaded

    This resume works pretty perfect – assume we leave on a friend's feed page. This page was reached via: Main Page, Friends page which passes parameters to the feed page (ID, name...).

    You can try this – and you'll see that these parameters are also persisted by the navigation framework. At a resume the page is called with the original parameters! And back is available two times in this situation.

    So we have a nice framework which does almost everything Microsoft asks us to do in the guidelines. Our problem – we must leave all of these pages and redirect the user to the login page. Even if we don't decide to implement a "stay online / remember login" we should at least handle resumes. Else we would end with:

    • User on friends page
    • Phone call
    • User accepts – our app is interrupted
    • User tells caller "I call back soon – just checking facebook"
    • User clicks back
    • User is brought back to friends page
    • We redirect to login page
    • ...

    We use a "normal token" with the facebook api. This means the token will be rejected after (about) 2 hours without usage. The compromise I choose is to store the information for 1 hour. If our app is started within that time we will restore the access_token. And since we need (for posts) also the user ID we will further store our logged on user in the database.

    The Database (eventual)

    We'll keep the things simple (no precise check for string length) and define a table like this using Visual studio data designer (this is provided by the database itself – but who cares). It looks like this:

    Figure 8: DB_USER Table

    DB_USER Table

    As you can see the table is very simple and the last column (STORE_TIME) is used to determine how old the record is. We need this to check for our "one hour limit".

    Per definition we can only have one entry here. So the data access code is very simple. By the way – if you worked with ADO.NET before you should already know the stuff. Yes there is no ADO.NET with WP7, but the guys from EffiProz built an (extended) provider for this. You remember – in the above code we used two functions DBHelpers.StoreDBUser and DBHelpers.DeleteDBUser – here they are:

    We use code which should be familiar for those of you who have use SQL Server (or similar) before. EnsureDBConnections will be handled later. The rest is creating a command if needed and later executing it.

    Notice the "brute force" DeleteDBUser() call in StoreDBUser. This ensures that only one record exists in the table. Of course I could have checked if a record exists – but this would be more time consuming than simply call a delete.

    When a new user is fetched (after logon) we store its values along with the access_token and when a user is logged of (set to null) we call the delete. There is one more function left – the one to load a stored user / token.

    Here by the way we could change the "stay logged on duration" by simply changing the command parameter. This function returns an object of type DBUser which is derived from the well known FBUser and just has one property more – the access_token. We use this when our application starts (App.xaml.cs)

    The noticeable thing here is in the property changed event handler from our current user holder. It changes a property in the (also new) FriendList. We met this guy above where I told you that our PGFriends is very simple in this version because it uses "background load" like we do in with our current user.

    The OwnerID property clears our friends list when a new OwnerID is set. This happens at logon / logoff and after a start / restart when we can load the current user from the database. But before we'll have a look at the Friend loader in detail let's demystify the EnsureDBConnection.

    The m_conDB is an DBConnection object. We keep this always open (in this case) and the function ensures that the connection exists and that it is opened.

    There is another function call herein – EnsureDBIsolotaedStorage. This brings us to our next "WP7 Problem". No, it's not a problem at all. It is just how the things work. By the way – they work similar for iPhone development.

    Resource or not Resource that’s the question

    Let me first show you a part of the solution:

    Figure 9: Project explorer view

    Project explorer view

    If we need files shipped with our application we can either embed them as resource or include them as content. You decide this via the file properties. If you want to include the as resource – select "Build Action" resource. Else choose this:

    Figure 10: File as content

    File as content

    Ensure that you have also set "Copy to Output Directory".

    Now what's the difference? Resources are loaded with the application which means that if you include large files it will increase your load time and memory consumption. On the other hand – such files are in memory and therefore available immediately.

    For content files the rule is that they are loaded (file by file) when they are needed. And they are discarded from memory if they are no longer needed. But how to address them?

    In images (Image control) this is easy just provide a relative path as source like /Images/UC.png. If you want to load media (images, sound,...) in code you can also use this by providing an URI with this path.

    In our case I decided to ship the database (2 Files in this scenario) as content files. There are some restrictions with content files; or the application directory on the device in general.

    You are not allowed to browse this directory (with iPhone you are allowed to browse) and the files are read only. But – the good news you can read files if you know their exact path and name.

    Since we want to change our data in the database we need the files on a location where they are writeable. Two strategies are common for this. Either include DB code to create (and pre fill) a new DB in your application. Or include readymade files and copy them to a location where we have write access. This must be done exactly once after application installation. My strategy: check if the files exist – if it doesn't exist then copy it. By the way – this code is the "device dependant" part of our DBHelpers class. The rest of the code would run on an iPhone, or a desktop application without any modification. To be more precise – it would run on any supported platform – and there are a lot – http://effiproz.pp-p.net just check it out.

    As you can see we use IsolatedStorage functions to access our local (writeable) storage. And for content files we use Application.GetResourceStream. This is the only way to access our files in code. And (remember) we have no functions to search for files – so we have to know what we ship with our application.

    A last note about database access: As you have seen we can access (writeable) files only in the IsolatedStorage. Now how do we tell our database to use this special location? There is no "normal path" and normal file functions don't work but luckily the EffiProz database provider knows about it. Our database files are now (after the copy) at IsolatedStorage.../DBs/ and have the name FBData... The connection string looks like this:

    Pretty easy and straight, isn't it? I finally left some other functions from DBHelpers. As you already know we also have a "FRIENDS" table. It is used in our "Friend loader" which I'll discuss in the next section.

    First the code of the functions:

    I use almost the same logic as with the DBUser database access. You may have noticed that there is a chance to "forget" friends in this table. You are right – we only delete (on updates) and keep the "old friends". And sometimes this is good – if you logon with a different user (repeatedly) you will already find your friends.

    To make this "bullet proof" you should also include a time column and extend the delete to something like

    And if we talk about "real world scenarios" I would mention that you should also think about caching feeds. In this case you would store feeds in the database and when you fetch them from facebook you only fetch feeds which are newer than the stored ones – this is easy with FQL.

    In our case - to show you in general how this could be done – I decided to cache our friends. The reason was that friends are a more stable thing than a feed list. Let's talk about friends.

    Friends (always remember them)

    Friends stay Friends (hopefully) and so we can assume that a friend list has some durability. Of course we find new friends from time to time – but in this case we can easily refresh our list. Here is (a part of) the code again:

    First the OnNavigateFrom handler; it get's called when the user navigates from us to somewhere. The opposite is OnNavigateTo which is called when the user navigates (from somewhere) to us. Form a code perspective this means "from" is called when we navigated to (somewhere) "to" is called when we navigation came to us.

    This handler clears the memory list to enforce a database reload – it sits here just for testing purposes. Since the friend list is a static object in the application (app) it will not lose its content while we change pages.

    In the application bar button handler we call ReloadFriendsList (new friends maybe) and when the page loads we call LoadFriendsIfNeeded what does exactly what it says – it loads friends only when needed. Let's have a look at our Friend loader – friend list class:

    As you can see it implements INotifyPropertyChanged for data binding and the only special thing here is that it sets Friends to null if we get a new OwnerID (ID from current user). This ensures that if a user logs on he doesn't work with the friends of the previous user.

    The LoadEnabled handler finally is used to enable / disable UI elements. In our case we use it for the refresh button on our friends page. Now to the more interesting parts.

    This function (as others) is simply blocked with a Boolean. So it will not run while there is a download in progress. This is not perfect but you can easily use better things (lock, mutex...) to do this.

    If friends are already in memory it does nothing than an update for the output information (status strings).

    Else it starts a StopWatch to measure performance. Then it checks if we have friends in the database. If so we load it from there measure the time, update the status and done.

    If there are no friends in the database we do what we did before (in the old friends page) – we start a download. When the download finishes we reach the following handler:

    Here (beside error checks and unlocking via LoadEnabled=true) we deserialize our friend list, measure the web time and store our friends in the database. Then we reload it from there (an easy way to get sorted friends) and measure this time also. One thing is left – reloading:

    This function just starts our StopWatch and initiates a download from the web. It doesn't clear the existing friends so if the call fails or it takes some time the user will still see the "old" list.

    Notice for this class that it always displays in the friends page what it did and how long it took. To see the different results check out time after a new logon, after a refresh and also notice the difference when you open the page from the main page (where we force a DB load) or by going back from the friends feed page.

    This is the end

    I know I told this also at part two of the series. But now I'm (almost) sure that I covered the facebook API as good as I could. Of course there are other things at facebook (likes, comments, invites...) but their APIs are similar to those we used here.

    What’s left?

    While this sample provides a lot of interesting and useable techniques / code - it is not a thing you could take and put on the store. I guess it would (almost) pass requirements and tests but that's all.

    So before you sell it (and share your revenues with me) you must (at least) implement the following:

    • Code to check network errors. I do some checks via simple "error status display". In reality you should check for a connection, disable functions (buttons) and so forth
    • Code to check forms if they can be opened. Our "user / token caching" avoids problems here – but only for an hour. After this time you may land in a friend page without an access_token.
    • UI design – UI design – UI design. While the friends list may work the feed display is more than only poor.

    About the third party tools used here

    As I wrote in the beginning of this article I use two tools in this sample. You can easily remove these tools from the application (and build the functionality "by hand" or leave it out) but I think both tools are worth to use them. Here (again) some information about the components:

    • Telerik controls for Windows Phone 7. No pricing available at the moment. To download the free beta visit: http://www.telerik.com/products/windows-phone.aspx
    • EffizProz (cross platform) Database for WP7. Free for non commercial use. For commercial use pricing depends on what you buy (there are bundles available). For closer information visit: http://effiproz.pp-p.net

    Special promotion

    In the context of publishing this article I made an agreement with EffiProz which allows me to sell their Database products for a reduced price. You can (for a limited time) purchase them with reduced prices from our store located at: http://effiproz.pp-p.net

    To run the application

    Normally I include everything in the test code. In this special case I couldn't include the Telerik controls since they are not a normal trial (which I could include) – you have to download the betas from Telerik.

    You will get an error at FBUris.cs – this is where I deleted my application key and secret. Replace this (as in the other part of this series) with your own values.

    Be sure to check out the "resume" functionality by pressing the "home button" at the friends page followed by pressing the "back button".

    And finally don't forget to check the cool new icon :)

    Links

    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
  • Part3
  • <<  Previous Article Continue reading and see our next or previous articles Next Article >>

    About Manfred Pohler

    Sorry, no bio is available

    This author has published 5 articles on DotNetSlackers. View other articles or the complete profile here.

    Other articles in this category


    Android for .NET Developers - Location and Maps
    In Windows Phone and iOS getting the current position of the device in terms of latitude and longitu...
    Android for .NET Developers - Using Web Views
    In this article, I'll show a native app that contains a web-based view. The great news is that HTML ...
    Android for .NET Developers - Building a Twitter Client
    In this article, I'll discuss the features and capabilities required by an Android application to ta...
    Ref and Out (The Inside Story)
    Knowing the power of ref and out, a developer will certainly make full use of this feature of parame...
    Developing a Hello World Java Application and Deploying it in Windows Azure - Part II
    In this article we will see the steps involved in deploying the WAR created in the first part of thi...
    Top
     
     
     

    Discussion


    Subject Author Date
    placeholder About Feeds Ravdeep Singh Khanna 7/15/2011 3:37 AM

    Please login to rate or to leave a comment.