For those interested, I've provided a full sample project here for your reference.
Step 0: Create or Open your C# Windows Phone Project
This article assumes the reader already has a base understanding of using Visual Studio and has already created a Windows Phone 8 or 8.1 project inside of Visual Studio (any edition).Step 1: Add ZXing to your Project
Thanks to the makers of the ZXing library, the open source community has shared an amazing set of utility code to read and parse image data for barcodes. This solution makes use of the ZXing port over to .NET called ZXing.net.Additionally, the ZXing.net community has made their library available through NuGet, which makes adding it to your project a trivial matter. To use the NuGet package, open the package manager from the Project menu as shown in the following:
Once open, search for the ZXing.net package, and add it by clicking the checkbox associated with the platform being supported (WP8-Barcode in this example).
Once completed, this will install the required package into your project and add all required references.
Step 1 [Shortcut] – Install from Package Manager
Many of the more seasoned developers will know, you can simply add the project to your file by using the package manager command prompt instead.Install ZXing.net
Step 2 – Add Library Includes (‘using’) References to Page
In order to leverage the camera, image manipulation and ZXing libraries in your code, for ease of use, you’ll want to add references to the libraries to the top of your source code. The following will be required:using Microsoft.Devices;
using ZXing;
using System.Windows.Threading;
using System.Windows.Media.Imaging;
using System.Windows.Input;
Step 3 - Create XAML Screen as a Viewfinder
Before diving into the c# event handlers and such, lets setup a simple viewfinder where we can show the camera contents, and for this example, simply display whatever barcode we've processed/read. To do this, something similar to the following XAML would be required.<phone:PhoneApplicationPage
x:Class="WP8_Barcode.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="200"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="WP8 Barcode" Style="{StaticResource PhoneTextNormalStyle}" Margin="12,0"/>
<TextBlock Text="scan barcode" 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">
<Canvas x:Name="viewfinderCanvas" Margin="0,0,0,10">
<!--Camera viewfinder -->
<Canvas.Background>
<VideoBrush x:Name="viewfinderBrush">
<VideoBrush.RelativeTransform>
<CompositeTransform
x:Name="viewfinderTransform"
CenterX="0.5"
CenterY="0.5"
Rotation="90"/>
</VideoBrush.RelativeTransform>
</VideoBrush>
</Canvas.Background>
<TextBlock
x:Name="focusBrackets"
Text="[ ]"
FontSize="40" Visibility="Collapsed"/>
<TextBlock Canvas.Left="100" TextWrapping="Wrap" Text="Tap to read the Barcode" Canvas.Top="200" Width="233" FontFamily="Segoe UI"/>
</Canvas>
</Grid>
<StackPanel Grid.Row="2" Margin="20, 0">
<TextBlock Text="Barcode Type:"/>
<TextBlock x:Name="tbBarcodeType" FontWeight="ExtraBold" Text="[Please Scan]"/>
<TextBlock Text="Barcode Data:"/>
<TextBlock x:Name="tbBarcodeData" FontWeight="ExtraBold" TextWrapping="Wrap" Text="[Please Scan]"/>
</StackPanel>
</Grid>
</phone:PhoneApplicationPage>
Step 4 - Define the Private Local Variables Required
Several of the core objects we'll be using will have a scope beyond the method being executed, therefore we will need to define some private variables to hold pointers to these objects.
//private locals
private PhotoCamera _phoneCamera;
private IBarcodeReader _barcodeReader;
private DispatcherTimer _scanTimer;
private WriteableBitmap _previewBuffer;
Step 5 - Initialize the Camera and Add Event Handlers
This can be done anywhere in your code (for example on a button press), but for this example, I've added it to the OnNavigatedTo method.protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
// Initialize the camera object
_phoneCamera = new Microsoft.Devices.PhotoCamera(CameraType.Primary);
//event callbacks defined
_phoneCamera.Initialized += new EventHandler(cam_Initialized);
_phoneCamera.AutoFocusCompleted += _phoneCamera_AutoFocusCompleted;
CameraButtons.ShutterKeyHalfPressed += CameraButtons_ShutterKeyHalfPressed;
viewfinderCanvas.Tap += new EventHandler(focus_Tapped);
//Display the camera feed in the UI
viewfinderBrush.SetSource(_phoneCamera);
// This timer will be used to scan the camera buffer every 250ms and scan for any barcodes
_scanTimer = new DispatcherTimer();
_scanTimer.Interval = TimeSpan.FromMilliseconds(250);
_scanTimer.Tick += (o, arg) => ScanForBarcode();
base.OnNavigatedTo(e);
}
As shown above, the device's camera object needs to be initialized, callbacks assigned to the event methods (see later steps for each of these callback definitions), and a timer initiated to periodically get whatever is in the camera buffer. This could also be changed to only grab the camera buffer on an event of some kind (e.g. camera button pressed, screen tapped, etc.) but for this example, it has been just established as a periodic 250ms.Step 6 - Include an Autofocus Completed Event Handler
The viewfinder in this example includes a set of brackets '[ ]' in the UI to allow the user to define the point of focus when they tap the screen. Once the autofocus has been completed, this callback method is triggered to simply show where the brackets should appear on the page.void _phoneCamera_AutoFocusCompleted(object sender, CameraOperationCompletedEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(delegate ()
{
focusBrackets.Visibility = Visibility.Collapsed;
});
}
Step 7 - Include a Screen Tap Event Handler
To support focusing on a single point within the virtual viewfinder, we have to add an event handler for the screen tap event (see OnNavigatedTo method above for where the event is assigned).void focus_Tapped(object sender, System.Windows.Input.GestureEventArgs e)
{
try
{
if (_phoneCamera != null)
{
if (_phoneCamera.IsFocusAtPointSupported == true)
{
// Determine the location of the tap.
Point tapLocation = e.GetPosition(viewfinderCanvas);
// Position the focus brackets with the estimated offsets.
focusBrackets.SetValue(Canvas.LeftProperty, tapLocation.X - 30);
focusBrackets.SetValue(Canvas.TopProperty, tapLocation.Y - 28);
// Determine the focus point.
double focusXPercentage = tapLocation.X / viewfinderCanvas.ActualWidth;
double focusYPercentage = tapLocation.Y / viewfinderCanvas.ActualHeight;
// Show the focus brackets and focus at point.
focusBrackets.Visibility = Visibility.Visible;
_phoneCamera.FocusAtPoint(focusXPercentage, focusYPercentage);
}
}
}
catch (Exception ex)
{
_phoneCamera.Initialized += new EventHandler(cam_Initialized);
}
}
Step 8 - Include a Half-Press Camera Button Handler
As with the above, if our device supports half-presses from the camera button, use it to trigger the camera's autofocus.void CameraButtons_ShutterKeyHalfPressed(object sender, EventArgs e)
{
_phoneCamera.Focus();
}
Step 9 - Include some final Configuration in the Camera Initialized Event
Some final configuration can be added once we know the camera is initialized, and we can begin our camera scan timer and start attempting reads. Optionally, this timer can be removed and only await a camera press or screen tap.void CameraButtons_ShutterKeyHalfPressed(object sender, EventArgs e)
{
_phoneCamera.Focus();
}
Step 10 - Include Scan for Barcode Logic
We add this method to save temporary images to the camera buffer and we make calls to ZXing to decode the content within this buffer.private void ScanForBarcode()
{
//grab a camera snapshot
_phoneCamera.GetPreviewBufferArgb32(_previewBuffer.Pixels);
_previewBuffer.Invalidate();
//scan the captured snapshot for barcodes
//if a barcode is found, the ResultFound event will fire
_barcodeReader.Decode(_previewBuffer);
}
Step 11 - Add a Barcode Found Callback
This method is called whenever ZXing yields a result from the decoding attempt. This is where we can add logic to parse the barcode data, save it in memory, or even navigate away from the screen if we just wanted to scan a single barcode.void _bcReader_ResultFound(Result obj)
{
// If a new barcode is found, vibrate the device and display the barcode details in the UI
if (!obj.Text.Equals(tbBarcodeData.Text))
{
VibrateController.Default.Start(TimeSpan.FromMilliseconds(100));
tbBarcodeType.Text = obj.BarcodeFormat.ToString();
tbBarcodeData.Text = obj.Text;
//do something real with the scan (e.g. persist it to a the State or ViewModel object before you leave the page)
//PhoneApplicationService.Current.State["Text"] = obj.Text;
//Navigate to some other page if you're done scanning
//NavigationService.GoBack();
//NavigationService.Navigate(new Uri("/PageYouWantToGetResult.xaml", UriKind.Relative));
}
}
Step 12 - Include Cleanup Logic
With camera and device access on Windows Phone, it is ABSOLUTELY CRITICAL that we cleanup our variables once we're done with them. If omitted, it is possible for your camera to keep on capturing and processing data, leaving flashes flashing, and simply crash your application. We've chosen to include all the cleanup code in OnNavigatedFrom method, or is automatically called by the system when a user navigates away from this page.protected override void OnNavigatingFrom(System.Windows.Navigation.NavigatingCancelEventArgs e)
{
//we're navigating away from this page, be sure to run this cleanup or you'll have issues
_scanTimer.Stop();
if (_phoneCamera != null)
{
// Cleanup
_phoneCamera.Dispose();
_phoneCamera.Initialized -= cam_Initialized;
CameraButtons.ShutterKeyHalfPressed -= CameraButtons_ShutterKeyHalfPressed;
}
}
No comments:
Post a Comment