• laurent@lioncoding.com

Concevoir un lecteur de Code-barres avec Xamarin.Forms


Dans ce premier article concernant Xamarin.Forms, il sera question de mettre en place une application Cross-Platform pouvant scanner et lire les codes-barres et de pouvoir partager le résultat de scan via les applications disponibles sur le smartphone.

Création du projet Cross-platform Xamarin

Figure 1: Création du projet

Ajout des packages nuget à notre solution

Quatre packages nuget seront nécessaires pour mettre en place notre lecteur de code-barres:

  1. Autofac: Pour faire l’inversion de contrôle;
  2. ZXing.Net.Mobile: Pour le scan et la lecture des codes-barres;
  3. ZXing.Net.Mobile.Forms: Pour le scan et la lecture des codes-barres;
  4. Xamarin.Essentials: Pour partager le résultat de scan via les applications disponibles sur le smartphone.

Je ne présenterai pas les configurations concernant le premier package nuget à savoir Autofac car il mérite à lui seul un article. Faites donc un tour sur Github pour télécharger ou cloner le projet afin d’avoir plus de détails sur comment faire l’inversion de contrôle.

Initialisations et Configurations

Pour pouvoir utiliser les trois derniers packages présentés dans le tableau, il est impératif de les initialiser et d’accorder les permission nécessaires sur chaque plateforme de notre solution.

Projet Android

  • Initialisation et accord de permission dans le fichier MainActivity.cs
protected override void OnCreate(Bundle savedInstanceState)
{
    // ...
    ZXing.Net.Mobile.Forms.Android.Platform.Init();
    Xamarin.Essentials.Platform.Init(this, savedInstanceState);
    // ...
}

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
    global::ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
  • Activation de la permission d’allumage de la caméra et du Flash dans le fichier AssemblyInfo.cs
[assembly: UsesPermission(Android.Manifest.Permission.Flashlight)]
[assembly: UsesPermission(Android.Manifest.Permission.Camera)]

Projet IOS

  • Initialisation des packages dans le fichierAppDelegate.cs
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    // ...
    ZXing.Net.Mobile.Forms.iOS.Platform.Init();
    // ...
}

Codes du projet partagé

La vue et son code behind

Notre vue sera très simple. nous utiliserons une ImageButton qui permettra de naviguer vers la page de scan des codes-barres. En-dessous de cet bouton,se positionnera un Label qui affichera le résultat de scan.

Dans la barre de menu, nous placerons deux ToolbarItem dont le premier aura pour rôle d’effacer le résultat de scan s’il y en a et le second de le partager.

Voici le code source de notre vue:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="BarCodeReader.Views.MainMenuView"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    Title="{Binding Title}"
    BackgroundImage="bg.jpg">

    <ContentPage.ToolbarItems>
        <ToolbarItem
            Command="{Binding ClearResultCommand}"
            Icon="clear.png"
            Text="Clear" />
        <ToolbarItem
            Command="{Binding ShareResultCommand}"
            Icon="share.png"
            Text="Share" />
    </ContentPage.ToolbarItems>

    <ContentPage.Content>
        <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
            <ImageButton
                Aspect="AspectFit"
                BackgroundColor="Transparent"
                Command="{Binding ScanCommand}"
                FlexLayout.AlignSelf="Center"
                FlexLayout.Grow="1"
                HorizontalOptions="CenterAndExpand"
                Source="barcodescanner.png"
                VerticalOptions="CenterAndExpand">
                <VisualStateManager.VisualStateGroups>
                    <VisualStateGroup x:Name="CommonStates">
                        <VisualState x:Name="Normal">
                            <VisualState.Setters>
                                <Setter Property="Scale" Value="1" />
                            </VisualState.Setters>
                        </VisualState>

                        <VisualState x:Name="Pressed">
                            <VisualState.Setters>
                                <Setter Property="Scale" Value="0.8" />
                            </VisualState.Setters>
                        </VisualState>

                    </VisualStateGroup>
                </VisualStateManager.VisualStateGroups>
            </ImageButton>
            <Label
                Margin="10,20,10,0"
                FontAttributes="Bold"
                FontSize="18"
                HorizontalOptions="CenterAndExpand"
                HorizontalTextAlignment="Center"
                Text="{Binding ScanResult}"
                TextColor="#525ABB"
                VerticalOptions="CenterAndExpand" />
        </StackLayout>
    </ContentPage.Content>

</ContentPage>

Le code behind correspondant est :

using BarCodeReader.ViewModels;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace BarCodeReader.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class MainMenuView : ContentPage
    {
        private MainMenuViewModel viewModel;
        public MainMenuView()
        {
            InitializeComponent();
            BindingContext = viewModel = new MainMenuViewModel();
            RegisterMesssages();
        }

        private void RegisterMesssages()
        {
            MessagingCenter.Subscribe<MainMenuViewModel>(this, "NoScanResultToClear", (m) =>
            {
                if (m != null)
                {
                    DisplayAlert("Warning !", "No scan result to clear.", "OK");
                }
            });

            MessagingCenter.Subscribe<MainMenuViewModel>(this, "NoScanResultToShare", (m) =>
            {
                if (m != null)
                {
                    DisplayAlert("Warning !", "No scan result to share.", "OK");
                }
            });
        }
    }
}

Si vous disposez du code de ma solution disponible sur github, Il est à constater qu‘elle implémente le MVVM. Les MessagingCenter mis en place dans le code behind permettent de faire un certain nombre de traitement que notre futur View Model ne pourra pas faire; ceci car la méthode DisplayAlert() permettant d’afficher des boîtes de dialogue ne peut pas être appelée dans un View Model. On pourrait intégrer un package de boîte de dialogue dans notre solution mais cela n’est pas nécessaire.

Notre View Model

Il est important que vous téléchargiez le dépôt du projet sur github car notre View Model héritera d’une classe implémentant L’INPC(INotifyPropertyChanged ), qui ne sera pas présenté dans cet article.

Le code source complet du View Model:

using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Essentials;
using Xamarin.Forms;
using ZXing.Net.Mobile.Forms;

namespace BarCodeReader.ViewModels
{
    public class MainMenuViewModel : BaseViewModel
    {
        public MainMenuViewModel()
        {
            Title = "Barcode Reader";
            ScanCommand = new Command(async () => await Scannning());
            ClearResultCommand = new Command(ClearResult);
            ShareResultCommand = new Command(async () => await ShareResult());
        }

        private async Task ShareText(string text)
        {
            await Share.RequestAsync(new ShareTextRequest
            {
                Text = text,
                Title = "Share"
            });
        }

        // Share Scan result
        private async Task ShareResult()
        {
            if (string.IsNullOrEmpty(ScanResult))
            {
                MessagingCenter.Send(this, "NoScanResultToShare");
            }
            else
            {
                await ShareText(ScanResult);
            }
        }

        // Clear Scan result
        private void ClearResult()
        {
            if (string.IsNullOrEmpty(ScanResult))
            {
                MessagingCenter.Send(this, "NoScanResultToClear");
            }
            else
            {
                ScanResult = "";
            }
        }

        // Do scanning
        private async Task Scannning()
        {
            var scanPage = new ZXingScannerPage
            {
                Title = "Scanning...",
                BackgroundColor = Color.FromHex("#212223")
            };
            await App.Current.MainPage.Navigation.PushAsync(scanPage);
            scanPage.OnScanResult += (result) =>
            {
                // Stop scanning
                scanPage.IsScanning = false;

                // Pop to page and show the result
                Device.BeginInvokeOnMainThread(async () =>
                {
                    await App.Current.MainPage.Navigation.PopAsync();
                    ScanResult = result?.Text;
                });
            };
        }

        public ICommand ScanCommand { get; private set; }
        public ICommand ClearResultCommand { get; private set; }
        public ICommand ShareResultCommand { get; private set; }

        private string _scanResult;
        public string ScanResult
        {
            get { return _scanResult; }
            set { SetProperty(ref _scanResult, value); }
        }
    }   
}

La méthode Scannning() fait le scan et renvoie le résultat au Label de notre vue. La méthode ShareResult() ouvre une petite fenêtre montrant les applications sur le smartphone pouvant faire le partage du résultat de scan et la méthode ClearResult() efface ce dernier.

Résultat de notre travail

  • Notre vue après lancement de l’application

  • Au clic sur le bouton image au centre de l’écran, la page de scan apparaît:

  • J’ai scanné le code-barres d’un bouquin sur Xamarin et j’ai le résultat suivant:

  • Au clic sur le bouton de partage qui se trouve sur la barre de menu, nous avons ceci:

Ressources

Voici le code source de ce projet sur Github.


Commentaires