Reputation: 1
I need help with my .NET Maui Blazor Hybrid project. What I am trying to do is the following:
/users/links/{initiatorType}/{initiatorId}/{targetCode}/link
so for example the if a Trainer is logged in, his/her QR code would be:
/users/links/Trainer/1
I only do C# in Maui Blazor via blazorWebView, no native code.
I am unable to detect and retrieve the QR's string. Can anyone help me with a simple solution to this problem? Many thanks for any hints/directions.
OK, so I have the nugets : ZXing.Net.Maui.Controls
I added the BarcodeReader in MauiProgram.cs
builder.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
}).UseBarcodeReader();
The camera permissions in both Android and Ios added.
This is my razor page:
@page "/UserProfile"
<CustomButtonComponent OnClick="ScanQrCode">
Scan
</CustomButtonComponent>
<div">@_qrCodeResult</div>
<ErrorMessageComponent @ref="_errorMessage"/>
@code {
private string? _loggedUserId;
private User? _user;
private ErrorMessageComponent? _errorMessage;
private string? _url;
private string? _qrCodeResult = "Not Scanned";
private async Task ScanQrCode()
{
try
{
// Check permissions for camera
var status = await Permissions.CheckStatusAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
{
Logger.LogError("Camera permission denied.");
return;
}
}
// Use the MediaPicker for photo capture
var photo = await MediaPicker.CapturePhotoAsync();
if (photo == null)
{
Logger.LogError("No photo taken.");
return;
}
await using var stream = await photo.OpenReadAsync();
var result = await DecodeQrCodeFromStream(stream);
if (result != null)
{
_qrCodeResult = result.Text;
Logger.LogInformation($"QR Code result: {_qrCodeResult}");
// Handle the scanned QR code
// await SendLinkRequest(_qrCodeResult);
}
else
{
_qrCodeResult = "No QR code found.";
Logger.LogWarning("No QR code found.");
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Error scanning QR code: {Message}", ex.Message);
_qrCodeResult = "Error scanning QR Code";
}
}
// Method to decode QR code from stream
private async Task<Result?> DecodeQrCodeFromStream(Stream stream)
{
try
{
// Convert the stream to a byte[] (this is necessary for creating the LuminanceSource)
var imageBytes = await GetImageBytesFromStream(stream);
// Create the LuminanceSource from the image bytes
var luminanceSource = new RGBLuminanceSource(imageBytes, 2000, 2000);
// Decode the QR code using BarcodeReader
var barcodeReader = new BarcodeReaderGeneric();
return barcodeReader.Decode(luminanceSource);
}
catch (Exception ex)
{
Logger.LogError(ex, "Error decoding QR code: {Message}", ex.Message);
return null;
}
}
// Helper method to convert Stream to byte[]
private async Task<byte[]> GetImageBytesFromStream(Stream stream)
{
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
return ms.ToArray();
}
private async Task SendLinkRequest(string scannedUrl)
{
...
}
}
So far I get No QR code found error.
Upvotes: 0
Views: 53
Reputation: 1
OK guys, I got it sorted making a mix of Gerald’s: https://www.youtube.com/watch?v=2dllz4NZC0I
and Pavlos’s solutions: https://pavlodatsiuk.hashnode.dev/implementing-maui-blazor-with-zxing-qr-barcode-scanner
Created a CameraPage.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="FitnessPal.CameraPage"
xmlns:zxing="clr-namespace:ZXing.Net.Maui.Controls;assembly=ZXing.Net.MAUI.Controls">
<ContentPage.Content>
<Grid>
<!-- Dark background to simulate modal effect -->
<BoxView Color="Black" Opacity="0.9" />
<!-- Scanner View in a smaller container -->
<Frame Padding="1" CornerRadius="10" BackgroundColor="DodgerBlue" Opacity="0.9" WidthRequest="350"
HeightRequest="500"
HorizontalOptions="Fill" VerticalOptions="Fill" BorderColor="SkyBlue">
<Grid>
<zxing:CameraBarcodeReaderView
x:Name="CameraBarcodeScannerView"
IsDetecting="True"
BarcodesDetected="BarcodesDetected"
VerticalOptions="Fill"
HorizontalOptions="Fill" />
<!-- Close Button -->
<Button Text="Close" TextColor="DarkOrange" FontSize="24" Clicked="OnCloseClicked"
HorizontalOptions="Center"
VerticalOptions="End"
Margin="5" />
</Grid>
</Frame>
</Grid>
</ContentPage.Content>
</ContentPage>
Then added necessary code behind to the CameraPage.xaml.cs
using System.Diagnostics;
using ZXing.Net.Maui;
namespace FitnessPal;
public partial class CameraPage
{
public CameraPage()
{
InitializeComponent();
CameraBarcodeScannerView.Options = new BarcodeReaderOptions
{
Formats = BarcodeFormats.TwoDimensional,
AutoRotate = true,
Multiple = false
};
}
private TaskCompletionSource<BarcodeResult> _scanTask = new();
public Task<BarcodeResult> WaitForResultAsync()
{
_scanTask = new TaskCompletionSource<BarcodeResult>();
if (CameraBarcodeScannerView != null)
{
CameraBarcodeScannerView.IsDetecting = true;
}
Debug.WriteLine($"Status: {_scanTask.Task.Status}");
return _scanTask.Task;
}
private async void BarcodesDetected(object? sender, BarcodeDetectionEventArgs eventArgs)
{
try
{
if (_scanTask.Task.IsCompleted) return;
CameraBarcodeScannerView.IsDetecting = false;
_scanTask.TrySetResult(eventArgs.Results[0]);
Debug.WriteLine("Scan result: " + eventArgs.Results[0].Value);
await MainThread.InvokeOnMainThreadAsync(async () =>
{
if (Navigation?.ModalStack.Count > 0)
{
await Navigation.PopModalAsync();
Debug.WriteLine("Closed CameraPage modal.");
}
});
}
catch (Exception e)
{
Debug.WriteLine($"Error detecting barcode: {e.Message}");
}
}
private async void OnCloseClicked(object? sender, EventArgs eventArgs)
{
try
{
CameraBarcodeScannerView.IsDetecting = false;
if (Application.Current?.MainPage?.Navigation != null)
{
await Application.Current.MainPage.Navigation.PopModalAsync();
Debug.WriteLine("Camera Page Closed.");
}
else
{
Debug.WriteLine("Go to Main Page.");
}
}
catch (Exception e)
{
Debug.WriteLine($"Error closing modal: {e.Message}");
}
}
}
Then to be totally clear here is the rest of important changes: MainPage.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"
xmlns:local="clr-namespace:FitnessPal"
x:Class="FitnessPal.MainPage"
BackgroundColor="{DynamicResource PageBackgroundColor}">
<ContentPage.Content>
<Grid>
<!-- Add padding to respect the safe area -->
<Grid.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0" />
<On Platform="Android" Value="0" />
</OnPlatform>
</Grid.Padding>
<BlazorWebView x:Name="BlazorWebView" HostPage="wwwroot/index.html">
<BlazorWebView.RootComponents>
<RootComponent Selector="#app" ComponentType="{x:Type local:Components.Routes}"/>
</BlazorWebView.RootComponents>
</BlazorWebView>
</Grid>
</ContentPage.Content>
</ContentPage>
MainPage.xaml.cs - getting rid of the top navigation
public MainPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
}
App.xaml.cs
public partial class App
{
public App()
{
InitializeComponent();
}
protected override Window CreateWindow(IActivationState? activationState)
{
return new Window(new NavigationPage(new MainPage())) {Title="Fitness Pal"};
}
}
Obviously the MauiProgram.cs needs the UseBarcodeReader:
builder.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
}).UseBarcodeReader();
Permissions added to Android manifest and ios plist.
But the last and not least, use of the CameraPage in the Blazor razor page:
<CustomButtonComponent CssClass="btn-outline-fp-info btn-fp-normal" OnClick="ScanQrCodeAsync">
Scan
</CustomButtonComponent>
<div>@_qrCodeResult</div>
</div>
@code {
...
private string _qrCodeResult = "Not scanned";
private async Task ScanQrCodeAsync()
{
var scanResult = await GetScanResultsAsync();
Debug.WriteLine($"Scan result: {scanResult.Format} -> {scanResult.Value}");
_qrCodeResult = $"Type: {scanResult.Format} -> {scanResult.Value}";
}
private async Task<BarcodeResult> GetScanResultsAsync()
{
var cameraPage = new CameraPage();
await Application.Current?.MainPage?.Navigation.PushModalAsync(cameraPage)!;
return await cameraPage.WaitForResultAsync();
}
I did not remove the Debug lines but hey, you know what it was for ;)
For anyone interested, there you go. Now I can use it in my ‘target’ nethod that retrieves the QR code value, appends the rest to make it a valid endpoint adress and finally send the POST request to that API endpoint. Happy Coding !
Upvotes: 0