Barcode Scanning in Xamarin Forms

Hello All

Today I am going to cover how we can have Barcode Scanning in a Xamarin Forms whilst correctly utilising the MVVM pattern.

*Note: Version 2.4.1 of Zxing.Mobile for Android appears to be bugged and won’t scan correctly. If you encounter this issue, downgrade your android versions (only) to 2.3.2.

Requirements

There are a few requirements for this guide, mainly that you will need a PHYSICAL DEVICE in order to run a barcode scanner. The iOS simulator & Android emulators will not give you access to a camera (not even your webcam), so you will need an actual device.

If you want to run this on iOS you will NEED a Mac or access to a Mac (through the network). MacInTheCloud is a great solution for developers without access to a physical mac.

Overview

Before we jump into the code lets first talk about what we will be doing today. We are going to be using Zxing.Mobile.Forms to scan a barcode and implement a proper MVVM pattern using Prism.

There are 2 components to this post:

  • Barcode Scanning in Xamarin Forms
  • Using the MVVM pattern with the Scanning

I have found there is not a huge amount of information online (certainly not in Zxing’s docs) about Mobile Scanning using MVVM. All of ZXing’s examples are done in code behind which is not ideal!

I mentioned them earlier but we are going to be using the following 2 libraries:

I’m going to be working on Visual Studio For Mac (Community Edition), but you should be able to follow along if you are using Visual Studio on Windows. Without further ado, lets get coding!

Project Setup

If you are on windows you will have to create the project yourself, You can use this guide to help setup a new prism project: https://prismlibrary.github.io/docs/xamarin-forms/Getting-Started.html

For all of you mac users, make sure you have downloaded the following extension for VS Mac:

Prism Template Studio and Developer Toolkit (https://github.com/dansiegel/Prism-Templates)

You can install this extension by going to: Visual Studio Community > Extensions > Gallery > Search > Prism

Screen Shot 2018-07-13 at 14.46.41.png

Once you have this extension installed, restart VS Mac and create a new project!

Select the Prism template named Blank Prism Application (Official). You can choose from the following prism containers: AutofacDryIocUnity. I use DryIoc but this very much comes down to personal preference!

Screen Shot 2018-07-13 at 14.50.57.png

Give the new project a build and run to make sure everything is ok.

Installing Nugets

You should now install the following NuGet packages into all projects (Forms, Android, iOS), I’m using the following versions:

  • Xamarin Forms – 3.1.0.583944
  • Prism.DryIoc.Forms – 7.0.0.396
  • Zxing.Mobile – 2.4.1
  • Zxing.Mobile.Forms – 2.4.1

Your NuGet package folders should now look a little like this:

Screen Shot 2018-07-13 at 14.57.56.png

Lets get started by deleting the Main Page (from Views, ViewModels and App).Screen Shot 2018-07-13 at 15.04.03.pngScreen Shot 2018-07-13 at 15.01.36.png

Now lets create a new Xaml ContentPage called ScannerPage, create this file inside the Views folder. Now we need to create a ViewModel for our page, in the ViewModels folder create an empty cs class named ScannerPageViewModel.

Lets first setup the view model, import the following namespaces:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using Prism.Services;

Now make this class inherit from ViewModelBase, this is our base view model, if you are using the prism templates this will be automatically generated for you.

public class ScannerPageViewModel : ViewModelBase
{
    public ScannerPageViewModel(INavigationService navigationService) : base(navigationService)
    {
    }
}

Above the constructor create a region for properties, I like to make sub regions for Interfaces, Delegate Commands and actual Properties to make my code more readable.

#region Properties

#region - Interfaces
#endregion - Interfaces

#region - Delegate Commands
#endregion - Delegate Commands

#region - Properties
#endregion - Properties

#endregion Properties

Now lets register our scan page for navigation (we need to do this otherwise Prism won’t be able to access this page and your app will crash!). Go to the App.xaml.cs or App.cs and in the method RegisterTypes add the following line:

containerRegistry.RegisterForNavigation<ScannerPage>();

The RegisterTypes method should now look like this:

protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterForNavigation<NavigationPage>();
    containerRegistry.RegisterForNavigation<ScannerPage>();
}

Now lets also set the root page of our app to our scanner page. The best thing about Prism is without a doubt the NavigationService. At the end of the OnIntialized() method in the App class is the line:

await NavigationService.NavigateAsync("NavigationPage/MainPage");

Replace it with the name of our scanner page.

await NavigationService.NavigateAsync("NavigationPage/ScannerPage");
 For those of you who have never used prism before, the Navigation Service is a way of navigation throughout an app from anywhere, namely inside a view model. All you need to do is pass the string of the page you want to navigate to and boom (this page needs to be registered beforehand in the app class). You can embed pages in navigation pages, tabbed pages, master details, assign children and navigate to the bottom of a stack. This functionality is extremely powerful and I recommend anyone who isn’t using an MVVM helper in Xamarin Forms to immediately pickup Prism!
Anyway run the app quickly to make sure there are no errors and lets get into implementing our barcode scanner!

Initialising ZXing Mobile

Before we can use Zxing we will need to call the initialiser for both iOS and Android separately.

Initialising Android

Inside the Android project, open up the MainActivity.cs file.

Inside the OnCreate(Bundle bundle) method call the ZXing initialiser:

protected override void OnCreate(Bundle bundle)
{
    TabLayoutResource = Resource.Layout.Tabbar;
    ToolbarResource = Resource.Layout.Toolbar;

    base.OnCreate(bundle);

    global::Xamarin.Forms.Forms.Init(this, bundle);
    ZXing.Net.Mobile.Forms.Android.Platform.Init();
    LoadApplication(new App(new AndroidInitializer()));
}

We will also need to install a permissions handler in the MainActivity, anywhere in this class add the following override:

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
    global::ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}

Now finally we will need to give the app Camera Flashlight permissions in the AndroidManifest.xml file. You can find it in the Android project files within the Properties folder. Open up the Manifest and search for the 2 permissions in the search bar at the bottom of the page.

Screen Shot 2018-07-13 at 15.47.57.pngScreen Shot 2018-07-13 at 15.47.47.png

Now our Android app has initialised ZXing and has the appropriate permissions to access the devices camera!

Initialising iOS

Inside the iOS project open up the AppDelegate.cs  file.

In the FinishedLaunching(UIApplication app, NSDictionary options) method call the ZXing initialiser.

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    global::Xamarin.Forms.Forms.Init();
    global::ZXing.Net.Mobile.Forms.iOS.Platform.Init();
    LoadApplication(new App(new iOSInitializer()));

    return base.FinishedLaunching(app, options);
}

Now open up the info.plist and add the following entry (I usually edit these with Xcode as it will autofill the available options)

  • Privacy – Camera Usage Description (add whatever text you feel is necessary to the description

Screen Shot 2018-07-13 at 15.54.56.png

Now our iOS app has initialised ZXing and has the appropriate permissions to access the devices camera!

Implementing ZXing in XAML

Open up ScannerPage.xaml  and pull in the ZXing namespace:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
             x:Class="BarcodeScanningPrism.Views.ScannerPage"
             Title="{Binding Title}">

Now inside the ContentPage.Content tags you can add the following XAML (don’t worry I’ll explain it in a second).

<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
    <zxing:ZXingScannerView/>
    <zxing:ZXingDefaultOverlay/>
</Grid>

Here we have added a Grid to our content page, inside that grid we have put our barcode scanner and the ZXing overlay. You can implement scanning without the Overlay, but you must always have the ZXingScannerView. Before we hookup this code to our ViewModel lets do some sweet sweet housekeeping.

Since we are using a navigation bar, lets add a toolbar button to control the camera flash.

Add the following XAML outside of the ContentPage.Content flags:

<ContentPage.ToolbarItems>
    <ToolbarItem Text="Flash"/>
</ContentPage.ToolbarItems>

Add a clicked handler to the ToolbarItem, I called mine Handle_FlashButtonClicked

XAML

<ContentPage.ToolbarItems>
    <ToolbarItem Text="Flash" Clicked="Handle_FlashButtonClicked"/>
</ContentPage.ToolbarItems>

Code Behind

void Handle_FlashButtonClicked(object sender, System.EventArgs e)
{
    throw new NotImplementedException();
}

Now you should add an x:Name to each of the Zxing components in our Xaml. Usually doing this in MVVM is a big don’t, however we need to access some config properties that aren’t as easy to configure in xaml.

<zxing:ZXingScannerView x:Name="scanView"/>
<zxing:ZXingDefaultOverlay x:Name="scanOverlay"/>

Now in the code behind, add the following:

public ScannerPage()
{
    InitializeComponent();

    scanView.Options.DelayBetweenAnalyzingFrames = 5;
    scanView.Options.DelayBetweenContinuousScans = 5;
}

void Handle_FlashButtonClicked(object sender, System.EventArgs e)
{
    scanView.IsTorchOn = !scanView.IsTorchOn;
}

Here we have configured the Scanner to have a delay between analysing frames, this prevents the scanner from becoming overactive and putting warnings into the debug console. We have also hooked up our ToolbarItem to control the phones flashlight.

If you add the following line to the scanner:

<zxing:ZXingScannerView x:Name="scanView" IsScanning="true"/>

And run the app, it should build and show the barcode scanner! On iOS you should be asked for camera permissions before

In the next step we will setup our ViewModel and hook up the XAML.

Hooking up the ViewModel

Before we jump into the ViewModel, add the following code into your xaml, basically we are going to add all of the required binding to our Xaml and then create the ViewModel for that data.

<zxing:ZXingScannerView x:Name="scanView" 
                        IsScanning="{Binding IsScanning}" 
                        IsAnalyzing="{Binding IsAnalyzing}" 
                        Result="{Binding Result, Mode=TwoWay}" 
                        ScanResultCommand="{Binding OnBarcodeScannedCommand}"/>

<zxing:ZXingDefaultOverlay x:Name="scanOverlay" 
                           TopText="Hold your phone up to the barcode" 
                           BottomText="Scanning will happen automatically" 
                           ShowFlashButton="false" 
                           Opacity="0.9"/>

Now lets open up our ViewModel (ScannerPageViewModel) and create our data bindings!

Lets start by adding some properties. Inside the Interfaces region add the following lines:

#region - Interfaces

    private readonly INavigationService _navigationService;
    private readonly IPageDialogService _pageDialogue;
    private readonly IDeviceService _deviceService;

#endregion - Interfaces

Add the following into the Delegates region:

#region - Delegate Commands

    public DelegateCommand OnBarcodeScannedCommand { get; set; }

#endregion - Delegate Commands

And finally add the following into the Properties Region:

#region - Properties

    //Used in OnBarcodeScanned();
    bool _isProcessing = false;

    private bool _isAnalyzing = true;
    public bool IsAnalyzing
    {
        get
        {
            return _isAnalyzing;
        }
        set
        {
            if (SetProperty(ref _isAnalyzing, value))
            {
                //Do something
            }
        }
    }

    private bool _isScanning;
    public bool IsScanning
    {
        get
        {
            return _isScanning;
        }
        set
        {
            if (SetProperty(ref _isAnalyzing, value))
            {
                //Do something
            }
        }
    }

    private ZXing.Result _result;
    public ZXing.Result Result
    {
        get
        {
            return _result;
        }
        set
        {
            if (SetProperty(ref _result, value))
            {
                //Do something
            }
        }
    }

#endregion - Properties

Now we have all of the required properties and commands declared in our ViewModel. But we aren’t done yet, we still need to initialise our interfaces and we will also need to add some code to run when we scan something.

The following code will pull in our interface dependencies (prism will initialise them for us), Set our delegate command and add a handler and finally set the scanner running:

public ScannerPageViewModel(INavigationService navigationService, 
                                    IPageDialogService pageDialogService,
                                    IDeviceService deviceService) : base(navigationService)
{
    Title = "Scan";

    _navigationService = navigationService;
    _pageDialogue = pageDialogService;
    _deviceService = deviceService;

    OnBarcodeScannedCommand = new DelegateCommand(OnBarcodeScanned);

    _isScanning = true;
}

void OnBarcodeScanned()
{
}

All we need to do now is add some code to perform when we scan something! I came up with the following code to print out our barcode result to the debug console.

private void OnBarcodeScanned()
{
    _isAnalyzing = false;
    RaisePropertyChanged("IsAnalyzing");

    string message = String.Format("I read a barcode and found the value: {0}", Result.Text);

    Debug.WriteLine(message);

    _isAnalyzing = true;
    RaisePropertyChanged("IsAnalyzing");
}

So when we receive our barcode scanned event we are performing the following steps:

  • Stop the scanner from analysing (temporarily whilst we do something)
  • Do something with our scanning result, print it out in this case
  • Once we have finished performing actions, restarting the scanner.

Run this app and scan a barcode (i just google images barcodes and it works great!).

Here is the output from running the app on a device:

Screen Shot 2018-07-13 at 16.37.55.png

Now lets add some user interaction to the barcode scan. Lets present a popup saying that we read a barcode. We can use the IPageDialogueService (provided by prism) to do this easily in our ViewModel.

Prism offers the await-able method, which we will use:

await _pageDialogue.DisplayAlertAsync($TITLE, $MESSAGE, $CANCELBUTTON);
private async void OnBarcodeScanned()
{
    _isAnalyzing = false;
    RaisePropertyChanged("IsAnalyzing");

    string message = String.Format("I read a barcode and found the value: {0}", Result.Text);

    await _pageDialogue.DisplayAlertAsync("Scan!", message, "Good for you");

    _isAnalyzing = true;
    RaisePropertyChanged("IsAnalyzing");
}

Now run the app and scan a barcode again…

Oh no! the app crashed! 😱😱😱

If you look at the exception for this crash on iOS and Android you will see different messages:

iOS

UIKit.UIKitThreadAccessException: 
UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.

Android

Java.Lang.RuntimeException: 
Can't create handler inside thread that has not called Looper.prepare()

This error occurs because for whatever reason, the popup is not called on the correct thread. I’m not sure where the issue lies (Prism or ZXing) but we can get around this problem with the final interface I asked you to pull into this ViewModel, IDeviceService.

We can use IDeviceService to grab the main thread of the device and run OnBarcodeScanned().

Update OnBarcodeScanned() with the following:

private void OnBarcodeScanned()
{
    _deviceService.BeginInvokeOnMainThread(async () =>
        {
            _isProcessing = true;
            _isAnalyzing = false;
            RaisePropertyChanged("IsAnalyzing");

            string message = String.Format("I read a barcode and found the value: {0}", Result.Text);

            Debug.WriteLine(message);

            await _pageDialogue.DisplayAlertAsync("Scan!", message, "Good for you");

            _isAnalyzing = true;
            RaisePropertyChanged("IsAnalyzing");
        });
}

Now when we run our code, we will call _pageDialogue.DisplayAlertAsync() on the main thread instead of somewhere that will make our app crash!

Once you have made this change, run the app.

You should now see a popup that reads off the value of the barcode you just scanned!

iOS

IMG_0019.PNG

Android

Screenshot_20180713-183047.png

Now there is one final code change to make, I’ve noticed that on iOS you can scan something and see multiple popups, to guard against this we can use the _isProcessing bool that we defined earlier in our properties. When _isProcessing is true, we can prevent repeated calls to OnBarcodeScanned() from executing any code.

private void OnBarcodeScanned()
{
    if (!_isProcessing)
    {
        _deviceService.BeginInvokeOnMainThread(async () =>
        {
            _isProcessing = true;
            _isAnalyzing = false;
            RaisePropertyChanged("IsAnalyzing");

            string message = String.Format("I read a barcode and found the value: {0}", Result.Text);

            Debug.WriteLine(message);

            await _pageDialogue.DisplayAlertAsync("Scan!", message, "Good for you");

            _isProcessing = false;
            _isAnalyzing = true;
            RaisePropertyChanged("IsAnalyzing");
        });
    }
}

Now run the app again, you should be able to read barcodes and see no duplicate popups!

The End

Thats it for this blog, we now have Barcode Scanning in Xamarin Forms using the MVVM design pattern. I have added the completed solution to my GitHub, all links are at the end.

If this post helped you, please consider commenting and sharing so more developers can implement ZXing stress free in Xamarin Forms!

You can find the completed solution available on GitHub

You can find me on the following sites:

8 thoughts on “Barcode Scanning in Xamarin Forms

  1. Great article, thank you. I have a question. What about external scanner devices that are attached to a phone or tablet? What needs to be changed to implement that? Thanks again

    Like

    1. Hi Dbnex, I haven’t done anything with external scanners before but I have found a link to a guide that may or may not be helpful https://acaliaro.wordpress.com/2017/02/07/connect-a-barcode-reader-to-a-xamarin-forms-app-via-bluetooth/.

      With an external scanner, you are just changing the camera you are using (from in built to bluetooth) so you would need to connect to the scanner (handling all associated aspects such as permissions, connectivity). Then you could probably use it in a very similar way to how I have outlined in my article. I don’t think ZXing has anything in it to handle external scanners so you might have to get a bit creative!

      Like

  2. Thanks Alex, … One thing I noticed, actually two
    1. You are having a bug in code, you are setting _isAnalyzing property inside the IsScanning setter. Someone already noticed that I see in your github and created an issue.
    2. But more importantly, the OnBarcodeScanned event will not be raised if you scanned the barcode right after app was loaded. It only gets triggered once you tap on home or recents Android buttons (circle and square), then restore your app using recents (square button on Android screen).
    If you use Back button (triangle) and then restore your app, scanning will not work again. To get it to work, you have to again tap on either circle or square to restore your app, then if you do scan, it will trigger ONBarcodeScanned event.

    Like

    1. Hi Dbnex, I haven’t added any handling yet for navigation. For this example I just wanted to demonstrate what a scanning page would look like, I haven’t added some of the necessary calls to make when considering navigating around an app.

      This is something I would expect someone to add in their own time! (I’ve already added this handling to the scanner app I’m working on)

      Like

  3. Another question, why did you remove base.OnRequestPermissionResult(….) call in MainActivity OnRequestPermission override?

    Shouldn’t that be like this:
    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
    {
    base.OnRequestPermissionsResult(requestCode, permissions, grantResults); //article did not have this base call
    global::ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults); //db*
    }

    Like

    1. If you look at the docs for Zxing.Mobile, he doesn’t actually use base.OnRequestPermissionResult(…). https://github.com/Redth/ZXing.Net.Mobile.

      I’ve just looked at the code I put into production (as I did a POC, this article and then put the code into an actual project) and I am making the base call:

      public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
      {
      global::ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
      PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
      base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
      }

      An interesting thing for permissions. I’ve had a lot of issues getting android to scan (because it was never asking permissions). I ended up pulling in James Montemagnos permissions library (https://github.com/jamesmontemagno/PermissionsPlugin) into my project and using that to ask permissions and now I’m having no problems on android. Might be worth pulling this library in if you have issues with ZXing.

      Here is a link to the issue with ZXing I am referring to https://github.com/Redth/ZXing.Net.Mobile/issues/741.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s