Les QR Code sont maintenant utilisés de manière très large. On en trouve partout, et depuis la pandémie du COVID 19 il a été extrêmement mis en avant. Il faut bien avouer que c’est pratique et simple à utiliser et qu’il existe énormément de manières et d’outils pour les générer.
Tout naturellement, il devient important pour les développeurs d’applications mobiles d’être en mesure de les lire pour les exploiter. Il existe à cet effet énormément de bibliothèques pour iOS et Android qui permettent cela.
Aujourd’hui nous allons voir comment intégrer à une application .NET MAUI la lecture de QR Code. Grâce à .NET MAUI, vous allez être en mesure lire des QR Code pour iOS, Android, Windows et Mac et ce avec un seul code C# !
Référencer le composant de lecture
Nous allons utiliser le composant Camera.MAUI proposé par hjam40. Il est basé sur la bibliothèque « ZXing.Net » pour la lecture et le décodage de codes divers et variés (dont les QR Code, ça tombe bien). Les sources de ce composant sont disponibles sur GitHub.
Pour ajouter ce composant, vous devez aller dans la gestion des packages Nuget, rechercher « Camera.MAUI » et l’installer.

Vous devez ensuite enregistrer ce nouveau composant au niveau de l’application. Pour cela il suffit d’ajouter la ligne « .UseMauiCameraView() » dans le code C# du fichier « MauiProgram.cs »
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiCameraView()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
return builder.Build();
}
Modifier la version minimum du SDK pris en charge par l’application sous Android
Camera.MAUI ne prend pas en charge les applications Android dont le SDK est inférieur à API 25. Par défaut un projet MAUI règle ce paramètre sur 21, ce qui est trop ancien. Il faut donc modifier cette valeur dans le fichier « AndroidManifest.xml » placé dans la dossier « Platforms\Android » de votre projet :

Note : J’ai pour habitude avec les projets MAUI de stipuler au minimum Android 8.0 (API 26) et de laisser comme cible Android 13.0 (API 33), qui est la valeur par défaut des projets MAUI actuels.
Ajouter les autorisations pour Android
Toujours dans le fichier « AndroidManifest.xml » vous devez stipuler que vous demandez l’autorisation d’accès à la caméra. Le composant va s’occuper tout seul de faire les demandes pour les versions d’Android (récentes) qui nécessitent une demande explicite à l’utilisateur. A l’initialisation du composant la demande d’accès à la caméra sera effectuée et il faudra bien autoriser l’accès pour que le composant fonctionne. Dans le cas contraire, le composant ne s’affichera simplement pas.

Ajouter les autorisations pour iOS
Dans le fichier « info.plist » placé dans le dossier « Platforms\iOS » de votre projet, vous devez ajouter les 2 lignes XML suivantes :
<key>NSCameraUsageDescription</key>
<string>This app uses camera for...</string>
Ceci permet d’indiquer à l’utilisateur pourquoi vous souhaitez accéder à la caméra. iOS fera alors la demande d’accès à l’utilisateur lors de l’initialisation du composant.
Ajouter le composant à une page
Pour utiliser ce composant dans une page vous devez y référencer son espace de nom (dans l’exemple ci-dessous, j’ai utilisé « camera ») :
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:camera="clr-namespace:Camera.MAUI;assembly=Camera.MAUI"
x:Class="C4P_QRCodeReader_MAUI.MainPage">
</ContentPage>
Il suffit ensuite de placer le composant où vous le souhaitez dans votre structure UI dans le fichier XAML de la page.
<camera:CameraView
x:Name="CameraView"
Margin="30,30,30,0"
Loaded="CameraView_Loaded"
BarcodeDetected="CameraView_BarcodeDetected"
ZoomFactor="{Binding Source={x:Reference ZoomSlider}, Path=Value}">
<camera:CameraView.GestureRecognizers>
<TapGestureRecognizer x:Name="CameraViewTapGesture" Tapped="CameraViewTapGesture_Tapped" />
</camera:CameraView.GestureRecognizers>
</camera:CameraView>
Dans cet exemple (tiré de l’application d’exemple liée à cet article) le composant porte le nom de « CameraView », et sa propriété « ZoomFactor » est liée à la valeur d’un Slider dont le nom est « ZoomSlider ».
Pour initialiser correctement le composant vous devez répondre à son événement « Loaded » et pour réagir à la détection d’un code à l’événement « BarcodeDetected ».
Dans l’application d’exemple on force l’activation de l’autofocus via un tap sur le composant. Pour cela un « TapGestureRecognizer » est ajouté au composant.
Paramétrer le composant pour lire les QR Code
A un moment il faut indiquer au composant que sa mission consiste à détecter et lire des QR Codes. Nous allons réaliser ce paramétrage depuis le code C# de la page. Ce paramétrage peut se faire dans l’événement « Loaded » de la page.
CameraView.BarCodeOptions = new Camera.MAUI.ZXingHelper.BarcodeDecodeOptions
{
AutoRotate = false,
PossibleFormats = { ZXing.BarcodeFormat.QR_CODE },
ReadMultipleCodes = false,
TryHarder = false,
TryInverted = false
};
Il est tout à fait possible de référencer plusieurs formats en même temps. Camera.MAUI sera alors en mesure de les détecter ensemble. La liste des formats supportés est assez importante :
- Aztec 2D barcode format
- CODABAR 1D format
- Code 39 1D format
- Code 93 1D format
- Code 128 1D format
- Data Matrix 2D barcode format
- EAN-8 1D format.
- EAN-13 1D format.
- ITF (Interleaved Two of Five) 1D format
- MaxiCode 2D barcode format
- PDF417 format.
- QR Code 2D barcode format
- RSS 14
- RSS EXPANDED
- UPC-A 1D format
- UPC-E 1D format
- UPC/EAN extension format. Not a stand-alone format
- MSI
- Plessey
- Intelligent Mail barcode
- Pharmacode format
Quelques options supplémentaires permettent de gérer finement le comportement attendu et aussi la quantité de ressources utilisées (le nombre de thread de décodage par exemple). Je vous invite vivement à modifier ces valeurs et constater en live comment se comporte Camera.MAUI.
Quoi qu’il en soit, il ne faut surtout pas oublier d’activer la détection de codes en plaçant à « true » sa propriété « BarCodeDetectionEnabled ».
CameraView.BarCodeDetectionFrameRate = 10;
CameraView.BarCodeDetectionMaxThreads = 10;
CameraView.ControlBarcodeResultDuplicate = false;
CameraView.BarCodeDetectionEnabled = true;
Activer le composant
Le démarrage complet de Camera.MAUI se fait depuis son événement « Load » :
private async void CameraView_Loaded(object sender, EventArgs e)
{
if (CameraView.NumCamerasDetected > 0)
{
CameraView.Camera = CameraView.Cameras.First();
await CameraView.StopCameraAsync();
await CameraView.StartCameraAsync();
}
}
Si aucune caméra n’est disponible le composant restera invisible. Par défaut on utilise la 1ère caméra, mais il est possible de gérer le fait que votre machine possède plusieurs caméras (un PC sous Windows par exemple).
Sous Android, parfois, la qualité de l’image retournée est assez médiocre. Pour éviter ce problème il suffit au moment d’activer la caméra de la stopper (même si vous savez pertinemment qu’elle n’est pas activée) puis de la démarrer. C’est pourquoi dans le code ci-dessus on trouve les 2 commandes.
Récupérer le code lu
private async void CameraView_BarcodeDetected(object sender, Camera.MAUI.ZXingHelper.BarcodeEventArgs args)
{
Debug.WriteLine("[CAMERA VIEW] - BARCODE DETECTED");
await MainThread.InvokeOnMainThreadAsync(async () =>
{
var r = args.Result.FirstOrDefault();
if (r != null)
{
LastCode = r.Text;
Debug.WriteLine($"[CAMERA VIEW] - {r.Text}");
}
else
{
Debug.WriteLine("[CAMERA VIEW] - NO RESULT !");
}
});
}
Comme Camera.MAUI est capable de lire en une seule fois plusieurs codes (par exemple tous les codes présents sur une feuille) il remonte une liste de résultats. Dans cet exemple on ne gère que le 1er résultat. D’ailleurs lors du paramétrage on a bien stipulé qu’on ne veut pas de multi-scan par
ReadMultipleCodes = false;
Gérer le zoom et la torche
Il est possible de stipuler le facteur de zoom que l’on souhaite appliquer à l’image. Dans certains cas ce réglage est indispensable pour être en mesure de lire correctement un code. C’est la propriété « ZoomFactor » qui le permet. La valeur du zoom va de « MinZoomFactor » à « MaxZoomFactor ». Dans cet exemple le zoom a été paramétré de 1 à 5 pour des raisons de simplification, mais idéalement, dans la vraie vie, vous devrez utiliser les valeurs fournies par ces 2 propriétés.
Il est possible aussi d’allumer la torche via la propriété « TorchEnabled » qu’il suffit de placer à « true » pour allumer et (vous l’avez déjà compris) à « false » pour l’éteindre. Dans l’application d’exemple une petite image de torche permet d’effectuer ces opérations.
Application d’exemple
Cet article est fourni avec une application d’exemple. Elle met en œuvre tout ce qui a été vue précédemment et vous permettra de faire quelques tests avant d’inclure ce composant dans vos propres réalisations.

Cet exemple a été testé sous iOS, Android et Windows.
Tutoriel vidéo
Pour ceux qui sont à l’aise avec l’anglais, il y a un très bon tutoriel vidéo proposé par Gérald Versluis sur Camera.MAUI. Gérald propose d’ailleurs une très grande quantité de tutoriels sur .NET MAUI. Je vous recommande vivement d’aller explorer sa chaîne YouTube.
Conclusion
Grâce à ce composant il est extrêmement simple d’inclure dans les applications .NET MAUI un lecteur de QR Code. Il est en plus capable de lire d’autres types de codes ce qui permet d’ouvrir un maximum de possibilités.
Bonne découverte et bons développements.
Steph
Salut,
Super tuto ^^.
Je suis intéressé par un retour d’expérience pour scanner des QRCode dans un projet .Net 8 for iOS, (pas un projet MAUI).
Je suis en train de migrer mon projet Xamarin native de Xamarin.ios vers .Net 8.0 for iOS (17.2) et je me rend compte que ZXing ne gère pas .Net 8.
On a une myriade de NuGet pour lire/scanner du QRCode et du code bar depuis un projet MAUI mais je n’en vois aucun pour scanner des QRCode comme avec le MobileBarcodeScanner de ZXing sur le nouveau Xamarin native : .Net8.0-ios sans MAUI.
je serai bien intéressé par un retour d’expérience car si je peux éviter de réinventer la roue en recodant tout un view controller avec AVFoundation, je suis preneur 🙂 ^^.
Jérôme.