So far in this article, you have built a stand-alone
application that uses choosers and parts of the XNA Framework to manage
photos snapped with a Windows Phone camera. What's unique about the
Windows Phone 7 Framework, however, is that it encourages you to have
your application make use of built-in phone applications to perform
certain tasks. The Photo application, for example, provides you with
hooks you can use to make the functionality of an application like
PhotoCapture available to users without having to find it and explicitly
launch it. Those hooks are the Extras and the Share
commands, which are found on the context menus of each photo where the
context menu is displayed when you press the photo for a few seconds.
Both features are best
explained by actually observing them at work. With the real Windows
Phone 7 in hand, click the Pictures hub, select the "folder" to view the
photos from (Camera Roll, for example), and then select a specific
photo from the list. Click the ellipsis (...) at the bottom of the
screen. When the context menu appears, you'll see an Extras option
displayed at the very bottom. This Extras option is available for you to
integrate or tie into. What you will do in the next walkthrough is use
it to launch the PhotoCapture application so that you can choose a
picture, save it to your TwitPic, and share it with your Twitter
friends.
The context menu that pops up when you press the picture includes a Share command. The Share option is available when photos are being viewed as a list rather than one at a time. If you click the Share
command, you will notice that it offers several ways to share the photo
with the world. In the following section, you will learn to build an
application that will use TwitPic as a cloud store for the photos on the
phone.
1. Using Extras to Launch an Application
The Windows Phone 7
platform is all about providing the best possible phone usage experience
to consumers. When consumers look at a photo on the phone, they are
using an application known as a Single Photo Viewer (SPV) that provides
applications to extend the viewer's functionality via the Extras
command of the context menu. In this section, you will further enhance
the PhotoCapture application to take advantage of the Extras feature
within the Photos application. The PhotoCapture application will be
using PhotoChooserTask or CameraCaptureTask to allow the user to select or take a picture once it has been invoked via the Extras feature.
To integrate with SPV, an application needs the following:
An Extras.xml file in its root directory with markup to the Extras feature
Code to properly read and load or manipulate the photo selected in the Extras dialog
You will implement both of those steps in the next section.
1.1. Adding an Extras.xml File to the Project
Adding an Extras.xml
file is very straightforward; the only potential difficulty may be in
the fact that the content of that file has to be precise. Make sure you
either copy and paste this content from the source code available with
this book, or type it in very carefully.
Right-click the project name, select Add => New Item, and then select XML file. Make sure (this is important!) you name it Extras.xml.
Double-click the Extras.xml
file to open it. Paste the following contents inside that file, which
will enable the Windows Phone 7 framework to locate those applications
ready to implement the Extras functionality.
<Extras>
<PhotosExtrasApplication>
<Enabled>true</Enabled>
</PhotosExtrasApplication>
</Extras>
The contents of the file are not very important; however, in the Properties window, make sure to set the Build Action property to Content and the Copy To Output property to Copy Always for the this file (click the file and press F4 to bring up the Properties dialog).
Make sure you save Extras.xml, but otherwise you are ready to move on to the next step.
1.2. Adding Code to Navigate to a Photo
To properly retrieve the photo that the user selected through the Extras feature, the application must override the OnNavigatedTo event in MainPage.xaml.cs. The steps here show you how to do that:
Open MainPage.xaml.cs and add the following using statement to the top of the page:
using System.Windows.Navigation;
The reference to the Microsoft.Xna.Framework
assembly should still be in the project from the prior walkthroughs;
however, if you start a new project that implements the Extras
functionality, make sure to add a reference to that assembly and the
following using statement to properly refer to the Media Library. You
will also need System.Windows.Medi.Imaging to work with the image source.
using Microsoft.Xna.Framework.Media;
using System.Windows.Media.Imaging;
Paste the following OnNavigatedTo method. Note how the basic operation is that of reading a QueryString
passed in, determining if we have a value for the parameter token, and
then trying to retrieve the photo from the Media Library by that token
ID.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
try
{
IDictionary<string, string> queryStrings =
this.NavigationContext.QueryString;
if (queryStrings.ContainsKey("token"))
{
MediaLibrary library = new MediaLibrary();
Picture picture = library.GetPictureFromToken(queryStrings["token"]);
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(picture.GetImage());
WriteableBitmap picLibraryImage = new WriteableBitmap(bitmap);
imgPhoto.Source = picLibraryImage;
}
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(() => txtStatus.Text = ex.Message);
}
}
Deploy
the application to the phone. Then, from the phone's Start screen,
select the Pictures hub, pick any picture collection, and select an
individual picture of your choice. Click the ellipsis at the bottom of
the screen, and select Extras. You should see the PhotoCapture
application listed on the next screen that comes up. Clicking the
PhotoCapture application should start the application and load the
selected photo into the Image control—exactly the expected behavior.
In the next section, you will
walk through the steps needed to extend the Share dialog for the
photos. The concepts you use to extend the Extras and Share features are
very similar; the differences, as you will see shortly, are in the
details of the file name and the query string key.
2. Using Share to Upload PhotoCapture Snapshots to TwitPic
In this walkthrough, you will
make more changes to the PhotoCapture application to take advantage of
the Share extensibility feature within the Photos application. For
simplicity, the PhotoCapture application will load the selected image
onto its main screen. In the next section, you will complete the circle
and write code to send the image to the TwitPic cloud service for easy
reference from the Twitter messages.
As with your implementation of Extras, to extend the Share option to include it, an application needs the following:
An E0F0E49A-3EB1-4970-B780-45DA41EC7C28.xml
file in its root directory enabling the application's integration with
the Share feature (this is not a typo—the XML file must be named exactly
like that for the application to belong to the Share feature)
Code to properly read and/or share the photo selected in the Share dialog
You will implement both steps in the next section.
2.1. Adding an E0F0E49A-3EB1-4970-B780-45DA41EC7C28.xml File to the Project
Adding this strangely named
XML file is very straightforward; the only potential difficulty may be
the fact that the name is completely unreadable, so it would be best if
you could copy and paste it from the source code available for download
with this book.
Right-click the project name, select Add => New Item, and then select "XML file." Make sure (this is important!) you name it E0F0E49A-3EB1-4970-B780-45DA41EC7C28.xml.
While the contents of the file may not be very important, be sure to go to the Properties window and set the Build Action property to Content and the Copy To Output property to Copy Always for the this file (click the file and press F4 to bring up the Properties dialog).
Make sure you save this XML file before moving onto the next step.
2.2. Adding Code to Navigate to the Selected Photo
To properly retrieve the
photo that the user selected through the Extras feature, the application
must override the OnNavigatedTo event in MainPage.xaml.cs. The steps here show you how to do that:
If
you are continuing from the Extras walkthrough, you already have all
the necessary references and using statements in place. However, if you
were to start a new project, make sure that you have a reference added
to Microsoft.Xna.Framework and the following using statements are in place:
using System.Windows.Navigation;
using Microsoft.Xna.Framework.Media;
Paste the following OnNavigatedTo
method (or add to that method if you are continuing from the Extras
walkthrough). Note how the basic operation is that of reading a query
string passed in, determining whether there is a value for the FileId parameter, and then trying to retrieve the photo from the Media Library by that token ID.
try
{
IDictionary<string, string> queryStrings =
this.NavigationContext.QueryString;
if (queryStrings.ContainsKey("FileId"))
{
MediaLibrary library = new MediaLibrary();
Picture picture = library.GetPictureFromToken(queryStrings["FileId"]);
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(picture.GetImage());
WriteableBitmap picLibraryImage = new WriteableBitmap(bitmap);
imgPhoto.Source = picLibraryImage;
}
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(() => txtStatus.Text = ex.Message);
}
Deploy the application to the phone.
Now you're ready to test your
implementation so far. Just as you did for your Extras integration, from
the phone's Start screen, select the Pictures hub on your phone, pick
any picture collection, and select an individual picture of your choice.
Click the ellipsis (...) at the bottom of the screen, and select Share.
You should see Upload to PhotoCapture as one of the options that come up. Clicking that option should start the application and load the selected photo into the Image control—the behavior we expect.
Now you're ready to upload
the photos to TwitPic, which is the primary hosting service for photos
destined for Twitter, the hottest social media network today.
Silverlight in general and Silverlight on Windows Phone 7 in particular
differ from other applications in the fact that they rigorously enforce a
non-blocking user interface principle: everything, including
communications over the network, must happen asynchronously. TwitPic
cloud service provides a RESTful API that allows programmers to send
messages to that service as long as we conform to the expected message
format.
2.3. Adding an Upload Button to the UI
On the user interface front, you will need to add an additional button that will trigger the upload of the photo to TwitPic. Figure 16-3 illustrates one possible placement of this button. Name the button btnUpload, and set its caption to TwitPic.
2.4. Writing Code to Transfer an Image to TwitPic
Because network access on
Windows Phone 7 must be performed asynchronously, it takes quite a bit
of code to properly construct the RESTful web service request. Most of
the code, however, is repetitive, and all of the major points are
summarized in these step-by-step instructions.
Right-click the project name in Solution Explorer, select Add Reference, and then select System.Xml.Linq.
Add the following using statements to the top of the page:
using System.IO;
using System.Text;
using System.Xml.Linq;
Open MainPage.xaml.cs and paste the UploadPhoto
function written here. This will be the only function that will be
invoked when the photo upload needs to take place. This function sets
the URL and the type of the request, and then it invokes the
asynchronous BeginGetRequestStream, which packages the photo and the user credentials.
public void UploadPhoto()
{
HttpWebRequest request =
(HttpWebRequest)WebRequest.Create("http://twitpic.com/api/upload");
request.ContentType = "application/x-www-form-urlencoded";
request.Method = "POST";
request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback),
request);
}
Add the following code to define the asynchronous function GetRequestStreamCallback
that does all of the packaging of proper parameters; note that the
exact form of the message was dictated by TwitPic, and this method
simply conforms to it.
NOTE
The TwitPic API is
sensitive to even slightly malformed messages; be sure you copy/paste
this method from the source code that comes with this book instead of
manually retyping it and risking making a mistake.
private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
{
try
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
string encoding = "iso-8859-1";
// End the operation
Stream postStream = request.EndGetRequestStream(asynchronousResult);
string boundary = Guid.NewGuid().ToString();
request.ContentType = string.Format("multipart/form-data; boundary={0}",
boundary);
string header = string.Format("--{0}", boundary);
string footer = string.Format("--{0}--", boundary);
StringBuilder contents = new StringBuilder();
contents.AppendLine(header);
string fileHeader = String.Format("Content-Disposition: file; name=\"{0}\";
filename=\"{1}\"; ", "media", "testpic.jpg");
string fileData = Encoding.GetEncoding(encoding).GetString(imageBits, 0,
imageBits.Length);
contents.AppendLine(fileHeader);
contents.AppendLine(String.Format("Content-Type: {0};", "image/jpeg"));
contents.AppendLine();
contents.AppendLine(fileData);
contents.AppendLine(header);
contents.AppendLine(String.Format("Content-Disposition: form-data;
name=\"{0}\"", "username"));
contents.AppendLine();
contents.AppendLine("BeginningWP7");
contents.AppendLine(header);
contents.AppendLine(String.Format("Content-Disposition: form-data;
name=\"{0}\"", "password"));
contents.AppendLine();
contents.AppendLine("windowsphone7");
contents.AppendLine(footer);
// Convert the string into a byte array.
byte[] byteArray =
Encoding.GetEncoding(encoding).GetBytes(contents.ToString());
// Write to the request stream.
postStream.Write(byteArray, 0, contents.ToString().Length);
postStream.Close();
// Start the asynchronous operation to get the response
request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(() => txtStatus.Text = ex.Message);
}
}
Add the GetResponseCallback
function that will asynchronously receive the results of the upload
(Success or Fail) and parse that result out using LINQ to XML.
private void GetResponseCallback(IAsyncResult asynchronousResult)
{
try
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
// End the operation
HttpWebResponse response =
(HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
XDocument doc = XDocument.Parse(responseString);
XElement rsp = doc.Element("rsp");
string status = rsp.Attribute(XName.Get("status")) != null ? rsp.Attribute(XName.Get("status")).Value : rsp.Attribute(XName.Get("stat")).Value;
// Close the stream object
streamResponse.Close();
streamRead.Close();
// Release the HttpWebResponse
response.Close();
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(() => txtStatus.Text = ex.Message);
}
}
Now you need to call the UploadPhoto method when the user clicks the TwitPic button. Open MainPage.xaml in Design view, and double-click the TwitPicbtnUpload_Click method, paste the following line of code: button. Inside the
UploadPhoto();
You are now ready to run the
application. Set the Windows Phone 7 emulator as your deployment target,
click F5, and when the application comes up, click the camera button
(the first button on the Application Bar). Take a picture, accept it,
and then click the TwitPic button. If no errors were reported in the status TextBlock, you should see your image on TwitPic's web site, as shown in Figure 2.