アイキャッチ画像

.NET MAUIでZXingが再表示に動作しない時の対処方法について

.NET MAUIでバーコードリーダーを実装するためにZXingを利用することがあると思います。

しかし、再表示した場合、以下のように動作しないことがあると思います。当記事では、この問題に対する対処方法を紹介していきます。

再度表示させた時に動作しない画面(Windows)

iOSでは、カメラが停止しないことがあります。問題の詳細は以下で紹介していますので確認してみてください。

紹介環境

当記事は以下の環境で作成しています。

開発環境

  • Visual Studio 2022
  • .Net 8

パッケージ

  • ZXing.Net.Maui(パッケージのバージョン:0.4.0)
  • ZXing.Net.Maui.Controls(パッケージのバージョン:0.4.0)

ZXingについて

ZXingは、バーコードの読み込みや生成に利用することができます。

実際に利用してみた例が以下のようになります。以下の例ではバーコードを読み取り内容をラベルに表示させています。

<?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:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI.Controls"
             x:Class="SampleZXing.Pages.ZXingPage"
             Title="バーコード">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"></RowDefinition>
            <RowDefinition Height="100"></RowDefinition>
        </Grid.RowDefinitions>
        <zxing:CameraBarcodeReaderView
            x:Name="cameraBarcodeReader"
            Grid.Row="0"
            BarcodesDetected="cameraBarcodeReader_BarcodesDetected">
        </zxing:CameraBarcodeReaderView>
        <Label
            x:Name="lblText"
            Grid.Row="1"
            HorizontalOptions="Center"
            VerticalOptions="Center"></Label>
    </Grid>
</ContentPage>
using ZXing.Net.Maui;

namespace SampleZXing.Pages;

public partial class ZXingPage : ContentPage
{
    public ZXingPage()
    {
        InitializeComponent();

        cameraBarcodeReader.Options = new BarcodeReaderOptions
        {
            Formats = BarcodeFormats.TwoDimensional,
        };
    }

    /// <summary>
    /// バーコードの読み込み時
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void cameraBarcodeReader_BarcodesDetected(object sender, BarcodeDetectionEventArgs e)
    {
        /*
         * スレッドが異なるためエラーになります。
         * そのエラーを回避するために「this.Dispatcher.Dispatch()」を利用します。
         */
        this.Dispatcher.Dispatch(() =>
        {
            foreach (BarcodeResult barcodeResult in e.Results)
            {
                lblText.Text = barcodeResult.Value;
            }
        });
    }
}

動作させた画面が以下のようになります。

実装例の動作イメージ

再表示時に動作しない現象について

以下のようにShellに画面を登録した場合、再度画面を表示させるとCameraBarcodeReaderViewが表示されなくなります。以下の例ではZXingPageにCameraBarcodeReaderViewを利用しています。

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="SampleZXing.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:pages="clr-namespace:SampleZXing.Pages"
    BackgroundColor="#5555ff"
    Shell.FlyoutBehavior="Flyout"
    Title="SampleZXing">

    <ShellContent
        Title="Home"
        ContentTemplate="{DataTemplate pages:HomePage}"
        Route="HomePage" />

    <!-- ↓にバーコードのスキャンを実装します。 -->
    <ShellContent
        Title="バーコード"
        ContentTemplate="{DataTemplate pages:ZXingPage}"
        Route="ZXingPage" />
</Shell>

再度表示させた画面が以下のようになります。

再度表示させた時に動作しない画面(android)

Windowsの場合、カメラの表示場所がバツになります。

再度表示させた時に動作しない画面(Windows)

動作しない原因について

画面の再表示時にカメラの読み込みを行われないことが原因になっています。Shellでは画面を再表示する際に以前の状態を利用されますが、それが原因でカメラの読み込みが行われないようです。

つまり、対処するには表示時にカメラを読み込めるようにする必要があります。

対処方法① 画面読み込み時にCameraBarcodeReaderViewを追加する

画面読み込み時にCameraBarcodeReaderViewを追加する方法を紹介します。

手順① レイアウトに名前を指定します

CameraBarcodeReaderViewを追加するレイアウトに名前(x:Name)を指定します。すでにCameraBarcodeReaderViewを追加している場合は削除してください。以下の例ではGridに「gridBarcode」と名前を指定しています。

<?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:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI.Controls"
             x:Class="SampleZXing.Pages.ZXingPage"
             Title="バーコード"
             Loaded="ContentPage_Loaded"
             Unloaded="ContentPage_Unloaded">
    <Grid
        x:Name="gridBarcode">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"></RowDefinition>
            <RowDefinition Height="100"></RowDefinition>
        </Grid.RowDefinitions>
        <Label
            x:Name="lblText"
            Grid.Row="1"
            HorizontalOptions="Center"
            VerticalOptions="Center"></Label>
    </Grid>
</ContentPage>

手順② 画面読み込み時にCameraBarcodeReaderViewを追加します

画面読み込み時にレイアウトに対してCameraBarcodeReaderViewを追加していきます。画面読み込み終了時には追加したCameraBarcodeReaderViewを削除するようにしています。再度表示させたときにCameraBarcodeReaderViewの重複を防いでいます。実装した例が以下のようになります。

using ZXing.Net.Maui;
using ZXing.Net.Maui.Controls;

namespace SampleZXing.Pages;

public partial class ZXingPage : ContentPage
{
    private CameraBarcodeReaderView _cameraBarcodeReader;

    public ZXingPage()
    {
        InitializeComponent();
    }

    /// <summary>
    /// 画面読み込み時
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ContentPage_Loaded(object sender, EventArgs e)
    {
        /*
         * CameraBarcodeReaderViewを追加します。
         */
        _cameraBarcodeReader = new CameraBarcodeReaderView();
        _cameraBarcodeReader.Options = new BarcodeReaderOptions
        {
            Formats = BarcodeFormats.TwoDimensional,
        };
        _cameraBarcodeReader.BarcodesDetected += cameraBarcodeReader_BarcodesDetected; ;
        gridBarcode.Add(_cameraBarcodeReader, row: 0);
    }

    /// <summary>
    /// バーコードの読み込み時
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void cameraBarcodeReader_BarcodesDetected(object? sender, BarcodeDetectionEventArgs e)
    {
        /*
         * スレッドが異なるためエラーになります。
         * そのエラーを回避するために「this.Dispatcher.Dispatch()」を利用します。
         */
        this.Dispatcher.Dispatch(() =>
        {
            foreach (BarcodeResult barcodeResult in e.Results)
            {
                lblText.Text = barcodeResult.Value;
            }
        });
    }

    /// <summary>
    /// 画面読み込み終了時
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void ContentPage_Unloaded(object sender, EventArgs e)
    {
        /*
         * 画面読み込み終了のため、CameraBarcodeReaderViewを停止させます。
         */
        _cameraBarcodeReader.IsDetecting = false;
        gridBarcode.Remove(_cameraBarcodeReader);
    }
}

対処方法② 画面の登録方法を変更する

画面の登録方法に「Routing.RegisterRoute」を利用することで対応することができます。しかし、この方法は画面の遷移方法が変わりますので注意してください。

手順① 画面を登録します

「Routing.RegisterRoute」で画面を登録します。画面は登録はShellのコンストラクタで行います。実装した例が以下のようになります。

using SampleZXing.Pages;

namespace SampleZXing;

public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();

        Routing.RegisterRoute("ZXing", typeof(ZXingPage));
    }
}

ShellのXaml側で画面を登録している場合は、以下のように削除しておいてください。

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="SampleZXing.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:pages="clr-namespace:SampleZXing.Pages"
    BackgroundColor="#5555ff"
    Shell.FlyoutBehavior="Flyout"
    Title="SampleZXing">

    <ShellContent
        Title="Home"
        ContentTemplate="{DataTemplate pages:HomePage}"
        Route="HomePage" />

    <!-- ↓にバーコードのスキャンを実装します。 -->
    <!--<ShellContent
        Title="バーコード"
        ContentTemplate="{DataTemplate pages:ZXingPage}"
        Route="ZXingPage" />-->
</Shell>

手順② 登録した画面へ遷移させる

登録した画面へ遷移させるには「Shell.Current.GoToAsync」を利用します。

実装した例が以下のようになります。以下の例ではボタンをクリックして遷移できるようになっています。

<?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="SampleZXing.Pages.HomePage"
             Title="HomePage">
    <VerticalStackLayout>
        <Label
            Text="Welcome to .NET MAUI!"
            VerticalOptions="Center"
            HorizontalOptions="Center" />
        <!-- ボタンのクリック時に画面遷移させます。 -->
        <Button Clicked="Button_Clicked"></Button>
    </VerticalStackLayout>
</ContentPage>
namespace SampleZXing.Pages;

public partial class HomePage : ContentPage
{
    public HomePage()
    {
        InitializeComponent();
    }

    /// <summary>
    /// ボタンクリック時
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private async void Button_Clicked(object sender, EventArgs e)
    {
        // 画面遷移を行います。
        await Shell.Current.GoToAsync("ZXing");
    }
}

ShellのXaml側との遷移方法の違いについて

ShellのXaml側で画面を登録した場合、ハンバーガーメニューでしたが、「Routing.RegisterRoute」で画面を登録した場合、以下のように戻るボタンになります。この画面遷移は、画面が親子関係ような遷移に利用されています。

「Routing.RegisterRoute」で画面に遷移した画面イメージ

おわりに

.NET MAUIで二次元バーコードの読み込みにZXingを利用していましたが、再度表示するときに動作しなくて困っていました・・・

当記事の内容を利用することで対応することができました。同じような境遇の方の役に立てば幸いです。