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:
- From the perspective of your program, the
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. - 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 .