Share via


Exercise 1: Capture Pictures Using the Camera Chooser

In this exercise you will:

  • Use the camera capture function to capture live pictures
  • Display captured pictures with their details

Task 1 – Capturing an Image

In this task you will use the camera capture chooser to capture a live image from both the WP7 emulator and device, and handle tombstoning situations while working with choosers.

Before you begin, please open the Source\Ex1 – CapturePictures\Begin.sln solution file, and review the code. The project is comprised of Models, Pages, Resources, the Application class, and the assets library.

Models in this case are passive data entities with no business logic. They are all derived from the NotifyingObject base class located in the AccessingWP7Devices.Assets project. The notifying object base class implements the .NET INotifyPropertyChanged interface for change notification, and provides minimal storage with generic get/set methods for holding the object’s state in a regular dictionary. In addition to models, there is a PictureRepository singleton object for handling and holding pictures. Currently the only thing it does is load the sample pictures, ResourcePicture, from the application resources into the pictures collection.

The Picture class represents a base class for captured pictures that you’ll create next, as well as for sample pictures located as resource in the resources folder.

To gain familiarity with the project, please build, deploy, and run it on the phone emulator.

Figure 1

First Run

The first page displays captured pictures. In our case, these are sample pictures for illustration. Clicking the camera app bar icon launches the Camera Capture built-in application, that lets you take pictures. You’ll implement this as your first task. Clicking the map app bar icon navigates to the Bing Map page. Next you’ll add a pictures layer and set your real location on this map. Clicking the picture app bar icon navigates to the pictures slide show, where you can start a slide show of the pictures you’ve captured.

  1. To capture a picture, use the camera capture chooser. Open the PictureListPage.xaml.cs file and add a new field for holding the camera capture task.

    Note:
    To ensure that your application receives the result of the Chooser task when your application is reactivated, the Chooser object must be defined within a global scope of the page and you must initialize the Chooser and assign the Completed event delegate in the page’s constructor.

    You can read more about the Launchers and Choosers in the Hands-on-Lab.

    C#

    private readonly CameraCaptureTask _cameraTask;

  2. Initialize the camera task in the page’s constructor and register its Completed event. Replace the constructor with the following:

    C#

    public PictureListPage() { DataContext = PictureRepository.Instance; InitializeComponent(); _cameraTask = new CameraCaptureTask(); _cameraTask.Completed += cameraTask_Completed; } private void cameraTask_Completed(object sender, PhotoResult e) { }
    Note:
    We want to display the camera task as first option when the camera app bar icon Is clicked.

  3. In the ApplicationBarNewPicture_Click event handler, call the camera chooser Show method.

    C#

    private void ApplicationBarNewPicture_Click(object sender, EventArgs e) { _cameraTask.Show(); }
    Note:
    The camera capture task completion event argument contains both the captured picture temporary file name and a temporary picture stream. To create a picture using these properties and holding additional metadata you should create a special model class.

  4. Add a new class called CapturedPicture to the project’s Model folder.
  5. Add the DataContract attribute to the class so you can serialize it later for tombstoning.

    C#

    [DataContract] public class CapturedPicture { }

  6. Create a public property of the type byte[], named ImageBytes for holding the picture bytes and decorate it using the DataMember attribute.
  7. Create a public property of the type string, named FileName for holding the picture file name and add the DataMember attribute.

    C#

    [DataContract] public class CapturedPicture { [DataMember] public byte[] ImageBytes { get; set; } [DataMember] public string FileName { get; set; }}

  8. Derive the CapturedPicture class from the Picture base class and override the CreateBitmapSource method to return a new WriteableBitmap using the picture bytes.

    Note:
    To create a WriteableBitmap from a stream use the PictureDecoder.DecodeJpeg helper method.

    C#

    [DataContract] public class CapturedPicture : Picture { ... protected override BitmapSource CreateBitmapSource() { BitmapSource source = null; if (ImageBytes != null) { using (var stream = new MemoryStream(ImageBytes)) { source = PictureDecoder.DecodeJpeg(stream); } } return source; } }

  9. Create a default constructor and a constructor that has two parameters: one for the file name and the second for the picture stream. Set the ImageBytes property with the stream bytes. Set the FileName property with the short file name without the path. Set the Picture.DateTaken property to DateTime.Now as a long date string.

    C#

    [DataContract] public class CapturedPicture : Picture { ... public CapturedPicture() { } public CapturedPicture(string capturedFileName, Stream capturedImageStream) { ImageBytes = ReadImageBytes(capturedImageStream); DateTaken = DateTime.Now.ToLongDateString(); FileName = System.IO.Path.GetFileName(capturedFileName); } private byte[] ReadImageBytes(Stream imageStream) { byte[] imageBytes = new byte[imageStream.Length]; imageStream.Read(imageBytes, 0, imageBytes.Length); return imageBytes; } }

  10. Now you are ready to handle the camera capture task. In the cameraTask_Completed event handler located in the PictureListPage class, check to confirm that the task completed successfully; if so, create an instance of the type CapturedPicture using the event arguments OriginalFileName and ChosenPhoto.

    C#

    private void cameraTask_Completed(object sender, PhotoResult e) { if (e.TaskResult == TaskResult.OK) { var capturedPicture = new CapturedPicture(e.OriginalFileName, e.ChosenPhoto); } }
    Note:
    Note: Opening a chooser causes the application to be deactivated. When a chooser finishes its job, the application is reactivated. Hence we should save the transient state (the captured picture in this case) before leaving the chooser and reload it when the application is reactivated.

  11. To save the CapturedPicture instance, use the TransientState.Set<T> method. The TransientState type is a helper class located in the assets project. It simply wraps calls to the Microsoft.Phone.Shell.PhoneApplicationService helper class, which handles the phone’s transient state, with generic get/set methods.

    C#

    private void cameraTask_Completed(object sender, PhotoResult e) { if (e.TaskResult == TaskResult.OK) { var capturedPicture = new CapturedPicture(e.OriginalFileName, e.ChosenPhoto); TransientState.Set("capturedPicture", capturedPicture); } }
    Note:
    Clicking “Back” while the camera chooser is active, closes the chooser and navigates back to the application directly to the last page visible by the user. In such cases, there may be no picture at all. If so, we want to stay on the same page if we have a picture to display. Otherwise, we want to navigate to the captured picture details page.

  12. To load the captured picture at the moment the page is loaded, override the Page.OnNavigatedTo method and use the TransientState helper class to restore the captured picture state.

    Note:
    When calling the TransientState.Get<T> method, make sure you pass ‘false’ as the last argument so that the captured picture is not removed from the state. You’ll extract it again later.

    C#

    protected override void OnNavigatedTo(NavigationEventArgs e) { var capturedPicture = TransientState.Get<CapturedPicture>("capturedPicture"); base.OnNavigatedTo(e); }

  13. Now that you’ve extracted the captured picture from the transient state, check if it is not null, then pass this state to the CapturedPicturePage page just before navigating to it (use TransientState.Set<T> and the CapturedPicturePage.ModelStateKey key that is expected by CapturedPicturePage); then navigate to the CapturedPicturePage.xaml page.

    Note:
    You can use the NavigationServiceExtensions.Navigate<TPage> extension method with the relevant page type to easily navigate to the required page.

    C#

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { var capturedPicture = TransientState.Get<CapturedPicture>("capturedPicture"); if (capturedPicture != null) { TransientState.Set<CapturedPicture>(CapturedPicturePage.ModelStateKey, capturedPicture); NavigationService.Navigate<CapturedPicturePage>(); } base.OnNavigatedTo(e); }

  14. To test the results, build and run the application. Click the camera icon in the app bar to open the camera capture chooser. Take a shot and click accept. The CapturedPicturePage opens.

    Figure 2

    Capture a Picture

  15. To test the phone’s Back button while the camera chooser is opened, go back to the main page, capture a picture, and now click Back. This should return to the main page.

Task 2 – Displaying a Captured Image

In this task you will create a simple view for displaying the captured picture, and provide a place for collecting user notes and displaying the date the picture was taken.

  1. To display the captured picture you should first extract the captured picture from the transient state, stored in Task 1. Open the CapturedPicturePage.xaml.cs file and add a new get property (this property will not have any setter) of the type CapturedPicture named Model.

    C#

    private CapturedPicture Model { get; }

  2. Implement the Model property by getting the value from the page’s DataContext. You’ll use this later to bind the page’s control with the picture. First check if DataContext is null; if so try to extract the picture from the transient state set by the previous page. If it is still null, set the DataContext with a fresh instance of the type CapturedPicture.

    C#

    private CapturedPicture Model { get { if (DataContext == null) { var model = TransientState.Get<CapturedPicture>(ModelStateKey); if (model == null) { model = new CapturedPicture(); TransientState.Set(ModelStateKey, model); } DataContext = model; } return DataContext as CapturedPicture; } }

  3. Override the Page.OnNavigatedTo and call the Model property. You will add code to this method later.

    C#

    protected override void OnNavigatedTo(NavigationEventArgs e) { var model = Model; base.OnNavigatedTo(e); }

  4. To test tombstoning, edit the note, lose focus, and then click the Windows button Followed by the Back button. You should notice that the picture and its details have been restored.

This concludes the exercise.

Note:
You can find the complete solution for this exercise at Source\Ex1 - CapturePictures\End.