Persisting an image

Windows Phone From Scratch

In our Full Stack program we need to take a snapshot and persist it to isolated storage, for PicturePicture retrieval at a later time.  This posed an interesting question: how do you put an image into a serializable form, yet reconstitute it to be the source for the Image control.

Let’s simplify the problem and strip it down to its essentials.  In this demonstration program we will interact with the UI shown in the figure.

[Click on the image to see full size]

The following controls are shown

  1. A TextBlock acting as a prompt
  2. A TextBox to gather the user’s name
  3. A Picture button to bring up the camera
  4. A Save button to save the user and the picture
  5. A Find button to locate the picture of the person whose name is in the text box

The Save and Find buttons start out disabled, and are enabled only when there is text in the text box.

When you click save, a person object is created and the person’s name and picture are stored to a dictionary (the same state they need to be in for persisting in isolated storage).  When you click find, if the person exists in the dictionary of people, then the person and picture are retrieved.

The Person object represents the name as a string and the image as a byte array,

public class Person
{
    public string FullName { get; set; }
    public byte[] Thumbnail { get; set; }
}

We begin the code section by creating a camera capture task, a dictionary to “persist” the person objects and a byte array to hold the thumbnail pending persistence,

private readonly CameraCaptureTask camera =
    new CameraCaptureTask();
private readonly Dictionary<string, Person> people =
    new Dictionary<string, Person>();
private byte[] thumbnail;

In the constructor we establish the event handlers for the buttons and the event handler for the camera’s asynchronous call back (called when the chooser returns).

public MainPage()
{
    InitializeComponent();
    camera.Completed +=  camera_Completed ;
    TakePicture.Click += ( o, e ) => { camera.Show(); };
    FullName.TextChanged += ( o, e ) =>
    {
        Find.IsEnabled = Save.IsEnabled =
            String.IsNullOrEmpty( FullName.Text ) ? false : true;
    };
    Save.Click += Save_Click;
    Find.Click += Find_Click;
}

Line 4 sets up the callback for the camera task

Line 5 is the event handler for the Picture button, it simply invokes the camera task

Lines 6-10 are the event handler for the TextChanged event in the text box, enabling and disabling our two buttons depending on whether or not there is text in the box.

Lines 11 and 12 set up the event handlers for the buttons.

Camera Completed

The Camera Completed task is responsible for retrieving the picture taken and displaying it to the user. We also take that opportunity to stash the picture into the member variable thumbnail for storage later when we have a Person object to store.

private void camera_Completed( object sender, PhotoResult e )
{
    var _imageBytes = new byte[e.ChosenPhoto.Length];
    e.ChosenPhoto.Read( _imageBytes, 0, _imageBytes.Length );
    e.ChosenPhoto.Seek( 0, SeekOrigin.Begin );

    Picture.Source = PictureDecoder.DecodeJpeg( e.ChosenPhoto );

    thumbnail = _imageBytes;
}

The process of making the image ready for display is to create a byte array of the size of the image as shown on line 3.

On line 4 we read the correct  number of bytes into that byte array and on line 5 we seek sto the start of the array.  We then assign to the Picture (image control) Source property the image created by calling the static method DecodeJpeg on PictureDecoder, passing in the photo as retrieved from the task.

When the Save button is clicked our job is to create a new Person object, populate it with the text from the TextBox and the byteArray we stored in the member variable thumbnail and then store the person into the Dictionary.

void Save_Click( object sender, RoutedEventArgs e )
{
    Person p = new Person()
    {
        FullName = FullName.Text,
        Thumbnail = thumbnail
    };
    Picture.Source = null;
    FullName.Text = "";
    people.Add(p.FullName,p);
}

While we are at it, we set the Picture (the image control) Source property to null, removing the picture from display and blank the text box – good feedback that the person has been saved.

When the Find button is clicked we check to see if the name in the TextBox matches a name in the dictionary.  If not, we show a messageBox that the name was not found.  If so, however, we retrieve that person object from the dictionary.

void Find_Click( object sender, RoutedEventArgs e )
{
    Picture.Source = null;

    if ( !  people.Keys.Contains(FullName.Text) )
    {
        MessageBox.Show(FullName.Text + " not found.");
        return;
    }
    var per = people[FullName.Text];

    if (per.Thumbnail == null)
    {
        Picture.Source = null;
        return;
    }
    byte[] data = per.Thumbnail;
    using (Stream memStream = new MemoryStream( data ))
    {
        WriteableBitmap wbimg =
            PictureDecoder.DecodeJpeg( memStream );
        Picture.Source = wbimg;
    }
}

If the Thumbnail property is null, there is no picture to display and we set the Source property of the Image control to null and return.  Otherwise, we create a local copy of the byte array held by the Person object which we pass into a new MemoryStream.  That memory stream is used to create a WriteableBitMap which is the source for displaying the image.

About Jesse Liberty

Jesse Liberty has three decades of experience writing and delivering software projects and is the author of 2 dozen books and a couple dozen online courses. His latest book, Building APIs with .NET will be released early in 2025. Liberty is a Senior SW Engineer for CNH and he was a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Microsoft MVP.
This entry was posted in Data, Mini-Tutorial, Patterns & Skills, WindowsPhone and tagged . Bookmark the permalink.

23 Responses to Persisting an image

Comments are closed.