Udostępnij za pośrednictwem


Creating a QR Code Reader App for Windows Phone 7

If you’ve updated Windows Phone 7.5, you might have noticed that it has some nice built-in functionality for reading QR Codes using Bing Vision search.  After trying it out for a while, I pretty quickly started wondering how I could use this in an app, and equally quickly I was disappointed to find that there are no APIs available for 3rd party developers to access this feature.

After a short hunt online, I found an excellent library that can be used as an alternative, and I built a simple reader with it.  Here are the basic steps to create one yourself:

 

NOTE: To build and test this example you must use a physical phone rather than the emulator, as you will need access to the camera feed.  

 

Create a new Windows Phone Project in Visual Studio

I used Visual Studio Ultimate, but the Express version that ships with the Windows Phone SDK will do just fine.  Use the basic phone template for this example:

image

Be sure to select ‘Windows Phone 7.1’ as the target OS:

image

 

Update the App Manifest

If you plan to submit a camera-based app to the marketplace, then you must ensure that the WMAppManifest.xaml contains this capability element:

 <Capability Name="ID_CAP_ISV_CAMERA"/>

A different element is required for the front-facing camera: ID_HW_FRONTCAMERA

Create the QR Reader Window and Output Area

Inside the content Grid in MainPage.xaml, add a Rectangle and a TextBlock:

 

 <!--TitlePanel contains the name of the application and page title-->
 <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
     <TextBlock x:Name="ApplicationTitle" Text="QR SCANNER"
                 Style="{StaticResource PhoneTextNormalStyle}"/>
     <TextBlock x:Name="PageTitle" Text="scanner" Margin="9,-7,0,0"
                 Style="{StaticResource PhoneTextTitle1Style}"/>
 </StackPanel>
  
 <!--ContentPanel - place additional content here-->
 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
     <!-- rectangle which acts as the 'camera window' -->
     <Rectangle Height="300" HorizontalAlignment="Left" Margin="82,61,0,0"
                 Name="videoRectangle" Stroke="White" StrokeThickness="5"
                 VerticalAlignment="Top" Width="300">
         <Rectangle.RenderTransform>
             <RotateTransform x:Name="videoRotateTransform"/>
         </Rectangle.RenderTransform>
         <Rectangle.Fill>
             <VideoBrush x:Name="viewfinderBrush"/>
         </Rectangle.Fill>
     </Rectangle>
             
     <!-- Textblock which will hold the output of the QR Code -->
     <TextBlock Height="171" HorizontalAlignment="Left" Margin="31,406,0,0"
                 Name="resultText" Text="&lt;nothing to display&gt;"
                 VerticalAlignment="Top" Width="399" TextWrapping="Wrap"
                 TextAlignment="Center" />
 </Grid>

 

This should result in a main page like this:

scanner

 

The Rectangle will act as the camera viewer – notice that it has a VideoBrush as its fill;  this will eventually contain the live feed from the camera.  The TextBlock will display the content read from the QR Code.

Directives and Variables

In MainPage.xaml.cs, add a few relevant using statements, such as for the Devices namespace so we can use the PhotoCamera type:

 using Microsoft.Devices;
 using System.IO;
 using System.Windows.Media.Imaging;
  

Declare some variables inside the main app class:

 private PhotoCamera camera;
 private bool capturing = false;

   

Handle Navigation

It is recommended practice to initialise and dispose of the camera object when we navigate to and from our page, respectively, so we need to override these methods:

 

  
 protected override void OnNavigatedTo(
            System.Windows.Navigation.NavigationEventArgs e)
 {
     if (null == camera)
     {
         camera = new PhotoCamera();
  
         // filred when the camera is initialised
         camera.Initialized += camera_Initialised;
  
         // fired when the button is fully pressed
         CameraButtons.ShutterKeyPressed += camera_ButtonFullPress;
  
         // fired when an image is available.
         camera.CaptureImageAvailable += camera_CaptureImageAvailable;
  
         // set the VideoBrush source to the camera output
         videoRotateTransform.CenterX = videoRectangle.Width / 2;
         videoRotateTransform.CenterY = videoRectangle.Height / 2;
         videoRotateTransform.Angle = 90;
  
         viewfinderBrush.SetSource(camera);
     }
  
     base.OnNavigatedTo(e);
 }
  
 // user navigated away from page
 protected override void OnNavigatedFrom(
            System.Windows.Navigation.NavigationEventArgs e)
 {
     if (camera != null)
     {
         // unhook the event handlers
         CameraButtons.ShutterKeyPressed -= camera_ButtonFullPress;
         camera.CaptureImageAvailable -= camera_CaptureImageAvailable;
         camera.Initialized -= camera_Initialised;
  
         // dispose of the camera object
         camera.Dispose();
     }
  
     base.OnNavigatedFrom(e);
 }

 

Here the PhotoCamera object is initialised to to use ‘primary’ camera, which is the rear-facing one. If you want to use the front-facing camera in an application, then you can call the alternate constructor like this:

 camera = new PhotoCamera(CameraType.FrontFacing);

 

Add Camera Event Handlers

The event handlers are quite simple and simply initialise some settings and respond to the user taking a picture:

 private void camera_Initialised(object sender, CameraOperationCompletedEventArgs e)
 {
     // set the camera resolution
     if (e.Succeeded)
     {
         var res = from resolution in camera.AvailableResolutions
                     where resolution.Width == 640
                     select resolution;
  
         camera.Resolution = res.First();
     }
 }
  
 // user has pressed the camera button
 private void camera_ButtonFullPress(object sender, EventArgs e)
 {
     if (capturing) return;
  
     capturing = true;
  
     camera.CaptureImage();
 }

We will add the ‘camera_CaptureImageAvailable’ handler later.

 

Download the Barcode Library

The library can be downloaded here:

https://twit88.com/platform/projects/show/mt-barcode

It ships with a sample Windows Phone 7 app, but this doesn’t read the data directly from the camera; instead it uses some hard-coded test images.  It also ships with an example desktop app for generating your own codes.

 

Add the Barcode Library to the Project

In MainPage.xaml.cs, add the following using statement:

 

 using MessagingToolkit.Barcode;

 

Add a reference to the Windows Phone 7 version of the library:

 

image

Decode the QR Code Image

The camera_CaptureImageAvailable event handler is called once we have an image to process. This is where we try to decode any possible QR Code it contains:

 

 private void camera_CaptureImageAvailable(
     object sender, 
     ContentReadyEventArgs e)
 {
     capturing = false;
  
     Stream imageStream = (Stream)e.ImageStream;
     BarcodeDecoder barcodeDecoder = new BarcodeDecoder();
  
     Dictionary<DecodeOptions, object> decodingOptions = 
                         new Dictionary<DecodeOptions, object>();
     List<BarcodeFormat> possibleFormats = new List<BarcodeFormat>(1);
     Result result;
  
     Dispatcher.BeginInvoke(() =>
     {
  
         WriteableBitmap qrImage = new WriteableBitmap(
                                         (int)camera.Resolution.Width, 
                                         (int)camera.Resolution.Height);
         imageStream.Position = 0;
         qrImage.LoadJpeg(imageStream);
  
         possibleFormats.Add(BarcodeFormat.QRCode);
  
         decodingOptions.Add(
             DecodeOptions.PossibleFormats, 
             possibleFormats);
  
         try
         {
             result = barcodeDecoder.Decode(qrImage, decodingOptions);
  
             resultText.Text = result.Text;
         }
         catch (NotFoundException)
         {
             // this is expected if the image does not contain a valid
             // code, Or is too distorted to read
             resultText.Text = "<nothing to display>";
         }
         catch (Exception ex)
         {
             // something else went wrong, so alert the user
             MessageBox.Show(
                 ex.Message, 
                 "Error Decoding Image", 
                 MessageBoxButton.OK);
         }
     });
 }

Compile and Deploy

As mentioned at the top of this post, you will need to deploy the app to a real phone, since the Emulator generates its own test output which is useless in this case.

That’s it.  You should now have a basic QR Reader.  Test it by reading the code below:

qrsample