Skip to content

Uploading an Image to Facebook in Windows Phone 7 (Silverlight)

This is a full-featured tutorial, walking through design, structure, UI and photo formatting. If you just want to know how to get from a Windows Phone 7  PhotoResult to posting that photo to Facebook, skip down to “Capture and Format the Photo” and start from there.

This post assumes you’ve gone through the process of setting up the permissions for a Facebook app and getting your user to log into Facebook through your app. In short, it assumes you’ve walked through this tutorial.

For this tutorial, I’ve re-jiggered the code into a more formal MVVM structure. I’m using MVVM Light because I am familiar with it. I would highly recommend it.

Download Facebook Message in Windows Phone 7 project files

Make sure you go to the App.xaml.cs file to change the FacebookAppID and FacebookAppSecret to fit your particular needs.

Design Considerations

When uploading an image either from the camera or from a saved image, we want to make sure the user has an good experience with it. This post will cover:

  • Taking the picture
  • Displaying the picture so the user can verify they picked the right one.
  • Adding a message (caption) to the picture.
  • Formatting that picture for a Facebook update.
  • Committing the update.

Setting Up Our Properties

We will set the following properties to be non-bindable (basic private properties)

PhotoResult _rawPhotoResult– this is what we get back from our phone when we take or select a picture. Rather than separate the bits out of it, we’ll just hold onto it in the view model for reasons that will soon become clear.

byte[] _fbImageHolder– this is the byte array of the image the user has selected. This is what we’ll upload to Facebook when we’re ready, but we wouldn’t use this to show to the user.

We’ll have the following Bindable properties (a property that implements that raises the PropertyChanged event when updated.

BitmapImage FBImagePreview– We can bind a BitmapImage to our UI, so we’ll use this BitmapImage to show the user what they are uploading.

string FBImageMessage– This is the caption for the image. We’ll use a TwoWay binding between this and a TextBox so the use can easily add some text.

bool HasImage– When the user opens the app, they won’t have an image selected, so we’ll want to show them some UI elements that let them start the selection process. HasImage will make that happen.

bool IsImageLoading– When the user opens the app, they won’t have an image selected, so we’ll want to show them some UI elements that let them start the selection process. HasImage will make that happen.

ICommand ProcessImageResult– We’ll call this when we come back from getting the photo. It will handle all the photo-crunching we need to see a preview of the image as well as put the image into our _fbImageHolder byte array.

ICommand CommitFacebookPhoto – This will do the actual commit of the image and the text to Facebook.

Setting Up Our UI

The biggest part of this UI is that we want the user to add a photo when there isn’t one and then see the photo when it is there.

For this, we’ll add a image to hold the FBImagePreview binding and a button to take the picture. We’ll turn the visibility for these elements on and off based on the “HasImage” property and we’ll add an event handler to Button so our XAML looks like this:

<Image Visibility=”{Binding HasImage, Converter={StaticResource BoolToVis}}” Source=”{Binding FBImagePreview}” Width=”300″ Height=”300″ HorizontalAlignment=”Left” Margin=”12,0,0,0″ /> <Button Visibility=”{Binding HasImage, Converter={StaticResource RevBoolToVis}}” Content=”take or select picture” Width=”300″ Height=”300″ HorizontalAlignment=”Left” Click

="TakePicture"/> 

We’ll deal with getting the picture in a moment. For now, we’ll just also make sure we have a TextBox set to the FBImageMessage binding and the Button bound to our CommitFacebookPhoto command. (I walked through how to set a command to an event in the previous Facebook tutorial post, so I won’t re-write it here.) Instead, I’ll just show the code I used for the TextBox and Button:

<TextBox TextWrapping=”Wrap” Text=”{Binding FBImageMessage, Mode=TwoWay}” MinHeight=”175″/> <Button Content=”post image to wall” Visibility=”{Binding HasImage, Converter={StaticResource BoolToVis}}”> <i:Interaction.Triggers> <i:EventTrigger EventName=”Click”> <GalaSoft_MvvmLight_Command:EventToCommand Command=”{Binding CommitFacebookPhoto, Mode=OneWay}”/> </i:EventTrigger> </i:Interaction.Triggers> </Button

> 

Capture and Format the Photo

In our “take or select picture button”, we call the “TakePicture” method in our code-behind. Here, we run standard code for getting or selecting an image.

private PhotoChooserTask _pct; private void TakePicture(object sender, RoutedEventArgs e) { _pct = new PhotoChooserTask(); _pct.ShowCamera = true; _pct.Completed += new EventHandler<PhotoResult>(_pct_Completed); _pct.Show(); }

Using the PhotoChooserTask with ShowCamera set to true means the user can either select a previously taken photo or take a new one. We set the Completed event handler and show task.

When the user has selected the image, we go to this event handler:

void _pct_Completed(object sender, PhotoResult e) { if (e.Error == null) _viewModel.ProcessImageResult.Execute(e); else MessageBox.Show(“Make sure your phone isn’t plugged into your computer before you add a picture.”, “No picture loaded”, MessageBoxButton.OK); }

For this to work, we’ll use a pretty standard trick in MVVM programming, which is to add the following in the View code behind so that we can call commands from code. Handy in situations exactly like this:

private MainViewModel _viewModel { get { return (MainViewModel)DataContext; } }

Inside the ProcessImageResult command, we want to set the FBImagePreview so that our user can see it and also get the _fbImageHolder byte[] ready so we’re all set to commit the image.

public ICommand ProcessImageResult { get { return new RelayCommand<PhotoResult>(result => { _rawPhotoResult = result; Deployment.Current.Dispatcher.BeginInvoke(() => { FBImagePreview = new BitmapImage(); FBImagePreview.SetSource(result.ChosenPhoto); HasImage = true; }); _fbImageHolder = ImageHelpers.FacebookImagePrep.SimpleFacebookPhotoCrunch(result.ChosenPhoto, 720, 0); }); } }

In the full project, you’ll see a more involved version of the image prep in which I use EXIF data to properly rotate the image. (Thanks to Tim Hueur for lighting the way on that count.) But for now we’ll just walk through a simple way to convert an image Stream to a byte[].

using System.Windows.Media.Imaging; using System.Windows.Media; using System.Windows.Controls; using System.IO;public static byte[] SimpleFacebookPhotoCrunch(Stream chosenPhoto, double maxSize, int rotation) { // Set a BitmapImage using the source stream BitmapImage bmp = new BitmapImage(); bmp.SetSource(chosenPhoto); // The largest permissable Facebook image upload is 720px (soon to be 920px) // This finds if the image width or height is largest and sets the resize // information accordingly double resizeRatio = 1; int resizeHeight = bmp.PixelHeight; int resizeWidth = bmp.PixelWidth; if (Convert.ToDouble(bmp.PixelHeight) > maxSize || Convert.ToDouble(bmp.PixelWidth) > maxSize) { if (bmp.PixelHeight > bmp.PixelWidth) { resizeRatio = maxSize / Convert.ToDouble(bmp.PixelHeight); resizeHeight = Convert.ToInt16(resizeRatio * Convert.ToDouble(bmp.PixelHeight)); resizeWidth = Convert.ToInt16(resizeRatio * Convert.ToDouble(bmp.PixelWidth)); } else { resizeRatio = maxSize / Convert.ToDouble(bmp.PixelWidth); resizeHeight = Convert.ToInt16(resizeRatio * Convert.ToDouble(bmp.PixelHeight)); resizeWidth = Convert.ToInt16(resizeRatio * Convert.ToDouble(bmp.PixelWidth)); } } // The default WriteableBitmap class lets us use a very simple // resize/rotation/quality JPEG extension and lets us output that as a byte[] WriteableBitmap writeBmp = new WriteableBitmap(bmp); using (MemoryStream ms = new MemoryStream()) { Extensions.SaveJpeg(writeBmp, ms, resizeWidth, resizeHeight, rotation, 100); return ms.ToArray(); } }

And that’s it! Our photo shows up in the UI for preview and we are ready to send the photo to Facebook.

Sending the Photo To Facebook

We should have the button command already set up for that, so all we need to do is write the details into the command.

public ICommand CommitFacebookPhoto { get { return new RelayCommand(() => { Deployment.Current.Dispatcher.BeginInvoke(() => { IsImageLoading = true; }); var facebookUpload = new Facebook.FacebookMediaObject { FileName = Guid.NewGuid() + “.jpg”, ContentType = “image/jpg” }; facebookUpload.SetValue(_fbImageHolder); var photoDetails = new Dictionary<string, object>(); photoDetails.Add(“message”, FBImageMessage); photoDetails.Add(“image”, facebookUpload); _asyncFbClient.PostCompleted += new EventHandler<FacebookApiEventArgs>(_asyncFbClient_ImagePostCompleted); _asyncFbClient.PostAsync(@”/photos”, photoDetails); }); } }

First, we turn on “IsImageLoading” so the user knows the app is  hard at work on this. Then we create a new FacebookMediaObject and set the value to our byte array. We set the details so Facebook knows we’re sending both a message and an image, then we add an event handler to handle the results. And away we go.

In our event handler, we check to see if the user cancelled the request (normally done by turning the phone off or otherwise disabling the app before the upload can complete) of if there were any other errors. If everything went right, we clear out the UI so the user can add another image if they so desire.

void _asyncFbClient_ImagePostCompleted(object sender, FacebookApiEventArgs e) { _asyncFbClient.PostCompleted -= _asyncFbClient_ImagePostCompleted; if (!e.Cancelled) { if (e.Error != null) { Deployment.Current.Dispatcher.BeginInvoke(() => { MessageBox.Show(“Crap. Something went wrong and it was: ” + e.Error.Message); IsImageLoading = false; }); } else { _fbImageHolder = null; Deployment.Current.Dispatcher.BeginInvoke(() => { FBImagePreview = null; FBImageMessage = “”; HasImage = false; IsImageLoading = false; }); } } else { Deployment.Current.Dispatcher.BeginInvoke(() => { MessageBox.Show(“Request was cancelled before upload could complete.”); IsImageLoading = false; }); } }

And that’s the end!