Redth / ZXing.Net.Maui

Barcode Scanning for MAUI?
MIT License
460 stars 151 forks source link

Missing MobileBarcodeScanner.Scan() equivalent or the ability to show scanner from code behind #157

Open Manish-Pradhan-FP opened 9 months ago

Manish-Pradhan-FP commented 9 months ago

In Xamarin Forms, we could easily trigger a Scanner simply by using Scan() and did not have to rely on creating our own view to embed this control. Is there a way I can achieve it currently or do I have to basically create my own XAML view and then embed the ScannerView ?

Previously this would work. I see no way of this now or am I missing something? ZXing.Result scanResult = await MobileBarcodeScanner.Scan();

if (scanResult != null) { xxamTableSearchEntry.Text = scanResult.Text; }

std66 commented 9 months ago

I came up with this one. This has one big issue, the camera remains active after completing the scan. If you try to use DisconnectHandler, an InvalidOperationException will be thrown. But I think this can give you a starting point.

Create a MAUI Class Library project and reference ZXing.Net.Maui.Controls. Add a new MAUI ContentPage (XAML) to the project.

XAML:

<?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"
             x:Class="SimpleScan.Maui.ScanPage"
             xmlns:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI.Controls"
             Title="ScanPage"
             Unloaded="OnUnloaded">
    <zxing:CameraBarcodeReaderView x:Name="barcodeView" />
</ContentPage>

XAML.cs:

namespace SimpleScan.Maui;

public partial class ScanPage : ContentPage {
    public event EventHandler<string>? BarcodeDetected;

    public ScanPage() : this(
        new global::ZXing.Net.Maui.BarcodeReaderOptions {
            Formats = global::ZXing.Net.Maui.BarcodeFormats.All,
            AutoRotate = true,
            Multiple = false,
            TryHarder = true
        }
    ) {

    }

    public ScanPage(global::ZXing.Net.Maui.BarcodeReaderOptions options) {
        InitializeComponent();
        barcodeView.Options = options;
    }

    protected override void OnAppearing() {
        barcodeView.BarcodesDetected += BarcodeView_BarcodesDetected;
        base.OnAppearing();
    }

    protected override void OnDisappearing() {
        barcodeView.BarcodesDetected -= BarcodeView_BarcodesDetected;
        base.OnDisappearing();
    }

    private void BarcodeView_BarcodesDetected(object? sender, global::ZXing.Net.Maui.BarcodeDetectionEventArgs e) {
        if (BarcodeDetected == null || e.Results.Length == 0)
            return;

        Dispatcher.Dispatch(() => BarcodeDetected?.Invoke(this, e.Results[0].Value));
    }

    private void OnUnloaded(object sender, EventArgs e) {
        //This will throw InvalidOperationException. I assume this is a bug in CameraBarcodeReaderView.
        barcodeView.Handler?.DisconnectHandler();
    }
}

Add a new C# class to the project:

using ZXing.Net.Maui;

namespace SimpleScan.Maui {
    public class SimpleZXingScanner {
        public async Task<string> ScanAsync(string title, Page parentPage, BarcodeReaderOptions options) {
            ScanPage page = new(options) {
                Title = title ?? "Scan barcode"
            };

            await parentPage.Navigation.PushAsync(page);

            TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
            page.BarcodeDetected += (o, e) => tcs.TrySetResult(e);

            string result = await tcs.Task;

            await parentPage.Navigation.PopAsync();

            return result;
        }
    }
}