2. Choosers
Now that you’ve seen the launchers, let’s look
at the choosers. The choosers present a similar development experience,
but unlike the launchers, they expect to return to your application
with some data. All the choosers support an event (usually called Completed
)
that is fired when the chooser is complete (whether it succeeds or
not). Because your application can be tombstoned when a chooser is
launched, you have to wire up this event in such a
way that it will be rewired when the application is untombstoned.
Typically, you would do this by having your task created at the page
level and wired up during page initialization (for instance, in the
constructor):
public partial class MainPage : PhoneApplicationPage
{
CameraCaptureTask cameraCapture = new CameraCaptureTask();
// Constructor
public MainPage()
{
InitializeComponent();
// Wire up completed event so it survives tombstoning
cameraCapture.Completed +=
new EventHandler<PhotoResult>(cameraTask_Completed);
}
...
}
When you launch a chooser, you can simply call Show
, like you did with the launchers in the earlier examples:
void cameraButton_Click(object sender, RoutedEventArgs e)
{
cameraCapture.Show();
}
In the event handlers of all the choosers, you must check the Error
property to ensure that an exception wasn’t thrown during the chooser operation. In addition, you should check the TaskResult
property on each chooser to ensure that the chooser was not canceled. You can check this by checking the TaskResult
against the TaskResult
enumeration:
void cameraTask_Completed(object sender, PhotoResult e)
{
if (e.Error == null && e.TaskResult == TaskResult.OK)
{
// ...
}
}
The following subsections provide examples showing how to use each chooser.
AddWalletItemTask
On the Windows Phone you can store payments
and deals in something called the Wallet. This task enables you to add
payment instruments (for example, accounts and/or credit cards) to the
Wallet. To do this, you must create an instance of the PaymentInstrument
class that contains the information about the payment type. Before you
can use this task, you will need to add several capabilities, including ID_CAP_WALLET_SECUREELEMENT
, ID_CAP_WALLET_PAYMENTINSTRUMENTS,
and ID_CAP_WALLET
.
First, you’ll need to create the task itself
directly at the class level because this task is a chooser. You should
handle the Completed event so that you can know whether the payment was
successfully added, like so:
public partial class MainPage : PhoneApplicationPage
{
AddWalletItemTask addWalletTask = new AddWalletItemTask();
// Constructor
public MainPage()
{
InitializeComponent();
addWalletButton.Click += addWalletButton_Click;
addWalletTask.Completed += addWalletTask_Completed;
}
...
}
After you have the task created, you need the data to send in with the task. First, create an instance of the PaymentInstrument
class, like so:
var item = new PaymentInstrument()
{
AccountNumber = "12345678",
BillingPhone = "(404) 555-1212",
DisplayName = "Wilder Minds Bank",
CustomerName = "Shawn Wildermuth",
ExpirationDate = new DateTime(2014, 12, 15),
PaymentInstrumentKinds = PaymentInstrumentKinds.CreditAndDebit,
Logo99x99 = logo99,
Logo159x159 = logo159,
Logo336x336 = logo336
};
In this case you can see
the typical financial data such as account number, billing phone, and
customer name. You can specify certain types of financial instruments
using the PaymentInstrumentKinds
enumeration. You can also see that the PaymentInstrument
does require three size logos. These are simply images of the right
size. You can create the logos directly from a remote or local resource,
as shown here:
// Logos
var logo99 = new BitmapImage(new Uri("/assets/logo99.png",
UriKind.RelativeOrAbsolute));
logo99.CreateOptions = BitmapCreateOptions.None;
var logo159 = new BitmapImage(new Uri("/assets/logo159.png",
UriKind.RelativeOrAbsolute));
logo159.CreateOptions = BitmapCreateOptions.None;
var logo336 = new BitmapImage(new Uri("/assets/logo336.png",
UriKind.RelativeOrAbsolute));
logo336.CreateOptions = BitmapCreateOptions.None;
Note that the logos are being created using the BitmapCreateOptions
value of “None.” This indicates that the URL should be passed as the bitmap, but it’s the responsibility of the AddWalletItemTask
or the Wallet UI to actually create these bitmaps when necessary.
Finally, you can create the task itself and set the new payment as the Item
property:
addWalletTask.Item = item;
addWalletTask.Show();
This way, when the task is shown, if the user
selects an item to add to their wallet, you can see whether the item is
added successfully in the event handler for the Completed
event.
void addWalletTask_Completed(object sender, AddWalletItemResult e)
{
if (e.TaskResult == TaskResult.OK)
{
MessageBox.Show("Payment Type Added");
}
}
Note that, even though the
Wallet feature supports both deals and payments, this task only supports
adding payments. You can use the Deal
class to add deals to the Wallet.
AddressChooserTask
Sometimes you might need to allow the user to retrieve a physical address from the address book. That is what the AddressChooserTask
is designed to do. You can wire up this task to let the user select an address:
AddressChooserTask addressChooser = new AddressChooserTask();
private void addressButton_Click(object sender, RoutedEventArgs e)
{
addressChooser.Show();
}
void addressChooser_Completed(object sender, AddressResult e)
{
if (e.Error == null)
{
string addressName = e.DisplayName;
string address = e.Address;
}
}
When the task completes, it returns both the
name of the address (the display name of the person/company in the
address book) and the address as a string. The address will have line
breaks embedded if it is multilined (as most are) so that they can be
displayed directly in a TextBlock
without additional formatting.
CameraCaptureTask
The CameraCaptureTask
is intended to instruct your user to take a picture and return to you the raw results of the picture:
CameraCaptureTask cameraCapture = new CameraCaptureTask();
void cameraButton_Click(object sender, RoutedEventArgs e)
{
cameraCapture.Show();
}
void cameraTask_Completed(object sender, PhotoResult e)
{
if (e.Error == null && e.TaskResult == TaskResult.OK)
{
// Create a BitmapImage from the photo
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(e.ChosenPhoto);
// Paint the background with the bitmap
ImageBrush brush = new ImageBrush();
brush.ImageSource = bitmap;
LayoutRoot.Background = brush;
}
}
In the event handler that is called after the user takes a picture, you are handed the photo (as the PhotoResult
’s ChosenPhoto
property) as a Stream
object. You can then use the results of the photo in any way you want
(for example, store it in isolated storage, upload it to a server, or
show it in the UI).
EmailAddressChooserTask
The purpose of this chooser is to enable the
user to select an email address from his list of contacts on the phone.
When you use this chooser, it shows only contacts with email addresses
to pick from, and if a contact has more than one email address, it will
let the user choose which one to use. To use the EmailAddressChooserTask
, you simply handle the event and show the task:
void chooseEmailTask_Click(object sender, RoutedEventArgs e)
{
// Pick an email address
task.Show();
}
void task_Completed(object sender, EmailResult e)
{
// Ensure no error and that an email was chosen
if (e.Error == null && e.TaskResult == TaskResult.OK)
{
MessageBox.Show(e.Email);
}
}
In the event handler that is called after a user picks an email, you can get at the email address via the Email
property of the EmailResult
object.
PhoneNumberChooserTask
Like the email chooser, this task is designed
to get the user to give you a phone number from his contacts. It works
in the same way as the email chooser:
void choosePhoneTask_Click(object sender, RoutedEventArgs e)
{
// Get Phone Number from Contacts
task.Show();
}
void task_Completed(object sender, PhoneNumberResult e)
{
// Ensure no error and that a phone number was chosen
if (e.Error == null && e.TaskResult == TaskResult.OK)
{
MessageBox.Show(e.PhoneNumber);
}
}
After the user has selected a phone number, the event returns with the selected phone number.
PhotoChooserTask
This chooser is used to let the user get a photo and return it to your application. At first glance, it works just like the CameraCaptureTask
, but instead it lets the user pick a picture from the ones stored on his phone:
void choosePhotoTask_Click(object sender, RoutedEventArgs e)
{
// Get a Photo
task.Show();
}
void photoChooser_Completed(object sender, PhotoResult e)
{
if (e.Error == null && e.TaskResult == TaskResult.OK)
{
// Create a BitmapImage from the photo
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(e.ChosenPhoto);
// Paint the background with the bitmap
ImageBrush brush = new ImageBrush();
brush.ImageSource = bitmap;
brush.Stretch = Stretch.None;
LayoutRoot.Background = brush;
}
}
You can enable the user to take a photo instead of selecting a photo from the phone by setting the ShowCamera
property on the task:
void choosePhotoTask_Click(object sender, RoutedEventArgs e)
{
// Get a Photo
task.ShowCamera = true;
task.Show();
}
If you specify that the ShowCamera
property is true
, the ApplicationBar
in the photo chooser will have a camera button to let the user take a photo instead.
The other option available to the chooser is to specify a photo size you need for your application:
void choosePhotoTask_Click(object sender, RoutedEventArgs e)
{
// Get a Photo
task.PixelHeight = 200;
task.PixelWidth = 300;
task.Show();
}
By specifying the PixelHeight
and PixelWidth
properties, you are telling the task to return the photo in that exact
dimension. When the user selects a photo that does not match that size,
he is presented with the ability to crop the photo to the selected size,
as shown in Figure 3.
FIGURE 3 Allowing photo cropping
SaveContactTask
You can also allow the user to save new contacts to the phone, using the SaveContactTask
. To do this, you specify the contact information in the SaveContactTask
object, like so:
private void saveContact_Click(object sender, RoutedEventArgs e)
{
saveContact.Company = "Tailspin Toys";
saveContact.FirstName = "Walter";
saveContact.LastName = "Harp";
saveContact.MobilePhone = "(206) 555-0142";
saveContact.PersonalEmail = "[email protected]";
saveContact.Show();
}
The SaveContactTask
has properties
to provide access to a large amount of a contact’s information,
including multiple phone numbers, multiple addresses, and a complete
name.
SaveEmailAddressTask
The SaveEmailAddressTask
is used
to save an email into a new contact or attach it to an existing contact.
To save an address, simply supply the email to save and call Show
:
void saveEmailTask_Click(object sender, RoutedEventArgs e)
{
// Save a new Email on the Phone
saveEmailTask.Email = "[email protected]";
saveEmailTask.Show();
}
void saveEmailTask_Completed(object sender, TaskEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("Error Occurred");
}
else if (e.TaskResult == TaskResult.Cancel)
{
MessageBox.Show("User Cancelled Task");
}
else
{
MessageBox.Show("E-mail Saved");
}
}
Note that the task does not tell you to which contact the email was attached but simply tells you whether the task succeeded.
SavePhoneNumberTask
This chooser works just like SaveEmailAddressTask
—you simply supply the phone number and then call the Show
method to save the phone number to a user’s contact:
void savePhoneTaskButton_Click(object sender, RoutedEventArgs e)
{
// Save a new Email on the Phone
savePhoneTask.PhoneNumber = "(404) 555-1212";
savePhoneTask.Show();
}
void savePhoneTask_Completed(object sender, TaskEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show("Error Occurred");
}
else if (e.TaskResult == TaskResult.Cancel)
{
MessageBox.Show("User Cancelled");
}
else
{
MessageBox.Show("Phone Number Saved");
}
}
}
Again, the TaskResult
will indicate
whether the task succeeded but does not share with your application the
contact to which the phone number was attached.
SaveRingtoneTask
If you’re in the business of selling ring
tones, your application can add ring tones to the phone. For a ring tone
to be added to the phone, you must use the SaveRingtoneTask
. A ring tone must conform to certain requirements:
• It must not be more than 39 seconds in length.
• It must be no more than 1MB in size.
• It can be in MP3 or WMA format only.
• It must not include DRM copyright protection.
You need to specify a DisplayName
(although the user can override it when the
task launches), a URI to the ring tone in isolated storage, and whether
the ring tone is shareable. Here is the relevant code:
void addRingtone_Click(object sender, EventArgs e)
{
saveRingtoneTask.IsShareable = true;
saveRingtoneTask.DisplayName = "Ahhh...";
saveRingtoneTask.Source = new Uri("isostore:/ahhh.mp3");
saveRingtoneTask.Show();
}
void task_Completed(object sender, TaskEventArgs e)
{
if (e.Error == null && e.TaskResult == TaskResult.OK)
{
MessageBox.Show("RingTone Added!");
}
}
The URI syntax used in this task specifies that
it is an absolute URI that begins with “isostore:/” to specify that it
is in your isolated storage. You can have structure to the location in
isolated storage, so “isostore:/ringtones/sounds/ahhh.mp3” is perfectly
acceptable. The return call to the Completed
handler assures you that there wasn’t an error and that the ring tone was successfully added.