Archive for February, 2012

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

No Comments »

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!


Posting To The Facebook Wall in Windows Phone 7 (Silverlight)

1 Comment »

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.

This is a full-featured tutorial, running through the view model and view setup for properly sending a Facebook message. If you just want to see the code for sending the message, skip down to “Sending the Facebook Message” at the bottom.

Setting Up Our Properties

Doing this the MVVM way is actually pretty simple. We’ll set up some properties in our view model and use those properties to drive our UI.

We will need:

FacebookMessage – string – This will hold the string message we want to post to Facebook.

IsLoadingMessage – bool – We’ll turn this on when the message is sending and turn it off when we’re done.

SendFacebookMessage – ICommand – We’ll execute this command when we’re ready to send the message out.

Setting Up Our UI

Again, very simple. We are going to add a ScrollViewer (I like to use ScrollViewers whenever the user might be typing so they can still see other bits of the screen) and put the rest of the stuff in there. We’ll add:

 1: <TextBox TextWrapping="Wrap"
 2:             Text="{Binding FacebookMessage, Mode=TwoWay}"
 3:             MinHeight="175"/>

 

We want the text to be bound to the message we’re going to send and the TwoWay mode makes sure that any updates we make in the UI are automatically synced with the view model property. I’m using a MinHeight as a way of implying to the user that this could be a multi-line message. Basically just prepping them for what kind of interaction to expect.

 1: <Button Content="post to wall"/>

 

We’ll use this button to run the command that starts all the work. The super-awesome way to do this is to go to the “Assets” tab in the top left corner of the screen, select “Behaviors” and drag an “EventToCommand” behavior onto your button.

image

select the EventToCommand behavior on the button and you should see on the right properties that can be set on that behavior. Click on the “Advanced options” (the little gray box next to the Command property”) and select “Data Binding”.

image

The Create Data Binding window will pop up. If you don’t see your command in the fields section, rebuild your project from Blend (under the Project menu) and it should refresh.

image

Select the SendFacebookMessage command and double check to see that the “EventName” is set to “Click”.

image

That should be the default, but it’s good to make sure.

Finally, we’re going to put together some UI for the user to get some feedback while the app is updating Facebook. We want two things:

  1. Not hit the button several times or change the message while it’s updating. In general, just leave everything alone.
  2. See that an update is happening.

In this project I did this very simply. First, I added some UI to cover all the elements (named “InteractionBlocker”).

 1: <Grid x:Name="InteractionBlocker"
 2:       Grid.Row="1"
 3:       Margin="0,48,0,0"
 4:       Background="#97000000"
 5:       Visibility="{Binding IsLoadingMessage, Converter={StaticResource BoolToVis}}"
 6:       />

 

When a message is updating, this grid simply covers the elements. It is semi-transparent, so the UI just goes a little dark while the update is being made. I’m using a BoolToVis converter, which can be used in a binding to convert a boolean value into a visibility value. Here is Kent Boogaart’s version of it, which isn’t the version I use, but works great none-the-less. (My version is in the project if you’re really interested.)

To let the user know that an update is going on, we’ll use a ProgressBar.

 1: <ProgressBar
 2:     Margin="0,4,0,0"
 3:     Grid.Row="1"
 4:     VerticalAlignment="Top"
 5:     Visibility="{Binding IsLoading, Converter={StaticResource BoolToVis}}"
 6:     IsIndeterminate="{Binding IsLoading}"
 7:     />

 

Here, we just set it to crawl across the top of the screen when we’re loading our message. We hide it and show it based on whether the user needs to see it. Pretty simple. (If you’re working on a big Windows Phone project, download and install the Silverlight Toolkit, which has a Progress bar that plays much nicer with your app performance. The default ProgressBar is good enough for our current needs.)

Sending the Facebook Message

We’ll do all our calls in the ICommand we’ve set up. When the user logged in, we created a private FacebookClient object with our access token, so we’ll just go ahead and use that.

 1: public ICommand SendFacebookMessage
 2: {
 3:     get { return new RelayCommand(() => {
 4:             var parameters = new Dictionary<string, object>
 5:             {
 6:                 {"message", FacebookMessage},
 7:                 {"privacy", new Dictionary<string, object>
 8:                     {
 9:                         {"value", "ALL_FRIENDS"}
 10:                     }
 11:                 }
 12:             };
 13:             _asyncFbClient.PostCompleted += new System.EventHandler<FacebookApiEventArgs>(_asyncFbClient_PostCompleted);
 14:             _asyncFbClient.PostAsync("me/feed", parameters);
 15:             Deployment.Current.Dispatcher.BeginInvoke(() =>
 16:             {
 17:                 IsLoadingMessage = true;
 18:             });
 19:         });
 20:     }
 21: }

 

What we’ve done here is set up our command so that we create the appropriate parameters for a normal boring old Facebook message, set up the acync callback so we actually get something and then, finally, jump over to the UI thread so the IsLoadingMessage property bindings make our UI do what we want.

So all we need to do now is handle the response from our post. We’ll do that with the following callback.

 1: void _asyncFbClient_PostCompleted(object sender, FacebookApiEventArgs e)
 2: {
 3:     if (!e.Cancelled && e.Error == null)
 4:     {
 5:         _asyncFbClient.PostCompleted -= _asyncFbClient_PostCompleted;
 6:         Deployment.Current.Dispatcher.BeginInvoke(() =>
 7:         {
 8:             IsLoadingMessage = false;
 9:             FacebookMessage = "";
 10:         });
 11:     }
 12:     else
 13:     {
 14:         Deployment.Current.Dispatcher.BeginInvoke(() =>
 15:         {
 16:             IsLoadingMessage = false;
 17:         });
 18:     }
 19: }

 

Here we’ve handled the response. If we wanted to be thorough, we’d catch any cancellations or errors in an else and display the results. But in this simple case, it either works and we’re all happy or it doesn’t work and the user is confused because the only indication that it didn’t work is that the FacebookMessage remains unchanged. On the bright side, they can always hit the “send message” button again and give it another try.


Follow me: matthiasshapiro