Accessing Bitmap Locally or Directly for Windows Phone 7 Application


This chapter is taken from book "Programming Windows Phone 7" by Charles Petzold published by Microsoft press. http://www.charlespetzold.com/phone/index.html

In a Silverlight program, you've seen that a bitmap added to the project as a resource is bound into the executable. It's so customary to reference that local bitmap directly from XAML, SilverlightTapToLoad project shows you how.

The MainPage.xaml.cs file requires a using directive for the System.Windows.Media.Imaging namespace for the BitmapImage class. Another using directive for System.Windows.Resources is required for the StreamResourceInfo class. When the screen is tapped, the event handler accesses the resource using the static GetResourceStream method defined by the Application class:

protected
override void OnManipulationStarted(ManipulationStartedEventArgs args)
{
    Uri uri = new Uri("/SilverlightTapToLoad;component/Images/Hello.png", UriKind.Relative);
    StreamResourceInfo resourceInfo = Application.GetResourceStream(uri);
    BitmapImage bmp = new BitmapImage();
    bmp.SetSource(resourceInfo.Stream);
    img.Source = bmp;
    args.Complete();
    args.Handled = true;
    base.OnManipulationStarted(args);
}
 

Notice how complicated that URL is! It begins with the name of the program followed by a semicolon, followed by the word "component" and then the folder and filename of the file. If you change the Build Action of the Hello.png file to Content rather than Resource, you can simplify the syntax considerably:

Uri uri = new Uri("Images/Hello.png", UriKind.Relative);

In a document entitled "Creating High Performance Silverlight Applications for Windows Phone," Microsoft has recommending using a Build Action of Content rather than Resource for assets included in your application to minimize the size of the binary and startup time. However, if these assets are in a Silverlight library that the program references, then it is better for them to be embedded in the binary with a Build Action of Resource.

If you have a number of images in your program, and you don't want to include them all in the XAP file, but you're nervous about downloading the images, why not do a little of both? Include low resolution (or highly compressed) images in the XAP file, but download better versions asynchronously while the application is running.

Capturing from the Camera

Besides embedding bitmaps in your application or accessing them from the web, Windows Phone 7 also allows you to acquire images from the built-in camera. The classes you use for this job are in the
Microsoft.Phone.Tasks namespace, which contains several classes referred to as choosers and launchers. Conceptually, these are rather similar, except that choosers return data to your program but launchers do not.

The CameraCaptureTask is derived from the generic ChooserBase class which defines a Completed event and a Show method. Your program attaches a handler for the Completed event and calls Show. When the Completed event handler is called, the PhotoResult event argument contains a Stream object to the photo. From there, you already know what to do. Here's the entire code-behind file:

using
System.Windows.Input;
using System.Windows.Media.Imaging;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Tasks; 
namespace SilverlightTapToShoot
{
    public partial class MainPage : PhoneApplicationPage
    {
        CameraCaptureTask camera = new CameraCaptureTask();
        public MainPage()
        {
            InitializeComponent();
            camera.Completed += OnCameraCaptureTaskCompleted;
        }
        protected override void OnManipulationStarted(ManipulationStartedEventArgs args)
        {
            camera.Show(); 
            args.Complete();
            args.Handled = true;
            base.OnManipulationStarted(args);
        }
        void OnCameraCaptureTaskCompleted(object sender, PhotoResult args)
        {
            if (args.TaskResult == TaskResult.OK)
            {
                BitmapImage bmp = new BitmapImage();
                bmp.SetSource(args.ChosenPhoto);
                img.Source = bmp;
            }
        }
    }
}

You can run this program on the phone emulator. When you tap the emulator screen, the call to Show causes the camera task to start up and you'll navigate to a page that resembles the actual camera. You can "shoot" a photo by tapping an icon in the upper-right corner of the screen. The simulated "photo" just looks like a large white square with a small black square inside one of the edges. Then you need to click the Accept button.

You can also run this program on the phone itself, of course, but not when the phone is tethered to the PC and the Zune software is running. After deploying the application to the phone using Visual Studio, you'll need to close the Zune software before testing the program.

When the SilverlightTapToShoot program calls the Show method on the CameraCaptureTask object, the SilverlightTapToShoot program is terminated. (Not immediately, though. The OnManipulationStarted method is allowed to return back to the program, and a couple other events are fired, but then the program is definitely terminated.)

The camera utility then runs. When the camera utility has done its job, the SilverlightTapToShoot program is re-executed. It's a new instance of the program. The program starts up from the beginning, the MainPage constructor is eventually called which sets the Completed event of the CameraCaptureTask to OnCameraCaptureTaskCompleted, and then that method is called.

For these reasons, the documentation advises that when you use a chooser or launcher such as CameraCaptureTask, the object must be defined as a field, and the handler for the Completed event must be attached in the program's constructor, and as late in the constructor as possible because once the handler is attached when the program starts up again, it will be called.

Phone's Photo Library

As you take pictures with the phone and synchronize your phone with the PC, the phone accumulates a photo library. A program running on the phone can access this library in one of two ways:

  1. From the perspective of your program, the
  2. PhotoChooserTask is much like the CameraCaptureTask except it takes the user to the photo library and allows the user to choose one photo, which is then returned to the program.
  3. The XNA namespace Microsoft.Xna.Framework.Media has a MediaLibrary and related classes that let a program obtain collections of all the photos stored in the photo library, and present these to the user.

I'm going to show you these two approaches with two programs. Just for variety (and to demonstrate how to use XNA classes in a Silverlight program), I'll use XNA for the first approach and Silverlight for the second.

You can run these two programs on the phone emulator. The emulator includes a small collection of photos specifically for testing programs such as these. When testing the programs on the actual phone, however, the phone must be untethered from the PC or the Zune software must be closed, because the Zune software won't allow simultaneous access to the phone's photo library. After you close Zune, you can run WPDTPTConnect32.exe or WPDTPTConnect64.exe program to allow Visual Studio to debug the program running on the phone.

The XnaTapToBrowse program requires a using directive for Microsoft.Phone.Tasks. It creates a PhotoChooserTask object along with the other fields:

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    Texture2D texture;
    PhotoChooserTask photoChooser = new PhotoChooserTask();
    ....
}

In compliance with the recommendations of the documentation, the class attaches a handler for the Completed event in the constructor:

public Game1()
{
    graphics = new GraphicsDeviceManager(this);
     Content.RootDirectory = "Content";
     // Frame rate is 30 fps by default for Windows Phone.
     TargetElapsedTime = TimeSpan.FromTicks(333333);
     TouchPanel.EnabledGestures = GestureType.Tap;
      photoChooser.Completed += OnPhotoChooserCompleted;
}

As usual, the Update method checks for user input. If a tap has occurred, the method calls the Show event of the PhotoChooserTask object:

protected override void Update(GameTime gameTime)
{
    // Allows the game to exit
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();
 
    while (TouchPanel.IsGestureAvailable)
        if (TouchPanel.ReadGesture().GestureType == GestureType.Tap)
            photoChooser.Show(); 
    base.Update(gameTime);
}
void OnPhotoChooserCompleted(object sender, PhotoResult args)
{
    if (args.TaskResult == TaskResult.OK)
        texture = Texture2D.FromStream(this.GraphicsDevice, args.ChosenPhoto);
}

The handler for the Completed event then creates a Texture2D from the stream available from the ChosenPhoto property. The Draw override doesn't attempt to render this object until it's available:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.Navy);
    if (texture != null)
    {
        spriteBatch.Begin();
        spriteBatch.Draw(texture, this.GraphicsDevice.Viewport.Bounds, Color.White);
        spriteBatch.End();
    }
    base.Draw(gameTime);
}

I'm using a slight variation of the Draw method of SpriteBatch here.

The SilverlightAccessLibrary program requires a reference to the Microsoft.Xna.Framework DLL, and you'll probably get a warning about including an XNA library in your Silverlight program. It's OK! The content area in the MainPage.xaml file contains both a bitmap-less Image and a text-less TextBlock in the Grid:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <Image Name="img" />
            <TextBlock Name="txtblk"
                       TextWrapping="Wrap"
                       TextAlignment="Center"
                       VerticalAlignment
="Bottom" />
</Grid>

Rather than present the entire photo library to the user (a task that would be a little difficult with only the rudimentary Silverlight layout elements I've described so far), the program picks one at random, and picks another when the user taps the screen:

public partial class MainPage : PhoneApplicationPage
{
    MediaLibrary mediaLib = new MediaLibrary();
    Random rand = new Random(); 
    public MainPage()
    {
        InitializeComponent();
        GetRandomPicture();
    }
    protected override void OnManipulationStarted(ManipulationStartedEventArgs args)
    {
        GetRandomPicture(); 
        args.Complete();
        base.OnManipulationStarted(args);
    }
    void GetRandomPicture()
    {
        PictureCollection pictures = mediaLib.Pictures;
        if (pictures.Count > 0)
        {
            int index = rand.Next(pictures.Count);
            Picture pic = pictures[index];
            BitmapImage bmp = new BitmapImage();
            bmp.SetSource(pic.GetImage());
            img.Source = bmp; 
            txtblk.Text = String.Format("{0}\n{1}\n{2}",
                                        pic.Name, pic.Album.Name, pic.Date);
        }
    }
}
 

The XNA MediaLibrary class is instantiated as a field. In the GetRandomPicture method, the program obtains a PictureCollection object from the MediaLibrary class and picks one at random. The Picture object has a GetImage method that returns a stream, and a Name, Album, and Data information that the program displays in the overlaying TextBlock.

Summary

I hope this article helps you to learn about how to accessing bitmap locally or directly for Windows Phone 7 Application .


Similar Articles