Skip to footer content
USING IRONBARCODE

How Do You Build a .NET MAUI Barcode Scanner in C#?

IronBarcode lets you scan barcodes directly from image files inside a .NET MAUI application -- no camera stream, no driver configuration, no platform-specific permission loops required.

You can scan barcodes from JPEG, PNG, GIF, TIFF, and BMP files with a single method call. The same code targets Windows, Android, and iOS without modification. Get started with a free trial to follow along with the code examples below.

How Do You Create a .NET MAUI Project for Barcode Scanning?

Setting up a .NET MAUI project in Visual Studio is straightforward. Launch Visual Studio 2022 or later, select Create a new project, choose the .NET MAUI App template, enter your project name, and select your target platforms. This tutorial focuses on Windows deployment, though the same project runs on Android and iOS. .NET MAUI is Microsoft's cross-platform framework for building native mobile and desktop apps with C# and XAML from a single shared codebase.

Unlike camera-based solutions such as ZXing.Net.MAUI that require CameraView control setup and MauiProgram.cs registration, IronBarcode needs no special configuration. Your MauiProgram.cs stays at the default template state. This keeps your startup code free from third-party handler registrations and reduces the surface area for initialization errors at launch time.

To install IronBarcode, run this command in the Package Manager Console:

Install-Package BarCode
Install-Package BarCode
SHELL

This single package gives you barcode scanning, QR code recognition, multi-barcode detection, and barcode generation. No additional dependencies are needed.

To activate IronBarcode in production, set your license key inside App.xaml.cs or MauiProgram.cs:

IronBarCode.License.LicenseKey = "YOUR_IRONBARCODE_LICENSE_KEY";
IronBarCode.License.LicenseKey = "YOUR_IRONBARCODE_LICENSE_KEY";
Imports IronBarCode

IronBarCode.License.LicenseKey = "YOUR_IRONBARCODE_LICENSE_KEY"
$vbLabelText   $csharpLabel

You can obtain a key from the IronBarcode licensing page or start with a free trial license.

How Do Permissions Differ for Image-Based Scanning?

Traditional camera-based barcode scanners require explicit permissions in platform manifests. On Android, you add to AndroidManifest.xml:

  • <uses-permission android:name="android.permission.CAMERA" />
  • <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

On iOS, you declare NSCameraUsageDescription in Info.plist. Handling denied permissions at runtime adds error paths that are easy to miss.

Because IronBarcode reads from a file stream rather than a camera preview, you only need file system access. On Windows this is granted automatically. On Android and iOS, FilePicker handles user consent when the user selects an image -- no manual permission requests required.

What XAML Interface Works Best for a MAUI Barcode Scanner?

A minimal interface -- an image picker button, an image display area, and a results label -- covers most barcode scanning scenarios. The following XAML creates that layout for a .NET MAUI ContentPage:

<?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="BarcodeScanner.MainPage">
    <ScrollView>
        <VerticalStackLayout Spacing="20" Padding="30">
            <Label Text="MAUI Barcode Scanner"
                   FontSize="24"
                   HorizontalOptions="Center" />
            <Button x:Name="SelectImageBtn"
                    Text="Select Image File"
                    Clicked="OnSelectImage" />
            <Image x:Name="SelectedImageDisplay"
                   HeightRequest="250" />
            <Label x:Name="ResultsLabel"
                   Text="Barcode results will appear here" />
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>
<?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="BarcodeScanner.MainPage">
    <ScrollView>
        <VerticalStackLayout Spacing="20" Padding="30">
            <Label Text="MAUI Barcode Scanner"
                   FontSize="24"
                   HorizontalOptions="Center" />
            <Button x:Name="SelectImageBtn"
                    Text="Select Image File"
                    Clicked="OnSelectImage" />
            <Image x:Name="SelectedImageDisplay"
                   HeightRequest="250" />
            <Label x:Name="ResultsLabel"
                   Text="Barcode results will appear here" />
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>
XML

The layout provides a button to trigger the file picker, a display area for the chosen image, and a label for decoded barcode values. It renders correctly across all .NET MAUI target platforms without platform-specific adjustments.

For production apps, consider replacing ResultsLabel with a CollectionView to display multiple barcode results in a scrollable list, especially when scanning documents that contain more than one barcode.

How Do You Scan Barcodes from Image Files in .NET MAUI?

The code-behind in MainPage.xaml.cs handles image selection and barcode reading. BarcodeReader.Read accepts a file path and returns a BarcodeResults collection. Each item in the collection exposes the barcode Value, BarcodeType, and position coordinates.

Here is the complete implementation:

using IronBarCode;
namespace BarcodeScanner;

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

    private async void OnSelectImage(object sender, EventArgs e)
    {
        try
        {
            // Open the system file picker filtered to image types
            var result = await FilePicker.PickAsync(new PickOptions
            {
                FileTypes = FilePickerFileType.Images,
                PickerTitle = "Select a barcode image"
            });

            if (result != null)
            {
                // Display the selected image in the UI
                var stream = await result.OpenReadAsync();
                SelectedImageDisplay.Source = ImageSource.FromStream(() => stream);

                // Decode all barcodes found in the image
                var barcodes = BarcodeReader.Read(result.FullPath);

                if (barcodes.Count > 0)
                {
                    // Build a display string listing each barcode type and value
                    string output = string.Join("\n",
                        barcodes.Select(b => $"{b.BarcodeType}: {b.Value}"));
                    ResultsLabel.Text = output;
                }
                else
                {
                    ResultsLabel.Text = "No barcodes detected in image";
                }
            }
        }
        catch (Exception ex)
        {
            ResultsLabel.Text = $"Error: {ex.Message}";
        }
    }
}
using IronBarCode;
namespace BarcodeScanner;

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

    private async void OnSelectImage(object sender, EventArgs e)
    {
        try
        {
            // Open the system file picker filtered to image types
            var result = await FilePicker.PickAsync(new PickOptions
            {
                FileTypes = FilePickerFileType.Images,
                PickerTitle = "Select a barcode image"
            });

            if (result != null)
            {
                // Display the selected image in the UI
                var stream = await result.OpenReadAsync();
                SelectedImageDisplay.Source = ImageSource.FromStream(() => stream);

                // Decode all barcodes found in the image
                var barcodes = BarcodeReader.Read(result.FullPath);

                if (barcodes.Count > 0)
                {
                    // Build a display string listing each barcode type and value
                    string output = string.Join("\n",
                        barcodes.Select(b => $"{b.BarcodeType}: {b.Value}"));
                    ResultsLabel.Text = output;
                }
                else
                {
                    ResultsLabel.Text = "No barcodes detected in image";
                }
            }
        }
        catch (Exception ex)
        {
            ResultsLabel.Text = $"Error: {ex.Message}";
        }
    }
}
Imports IronBarCode

Namespace BarcodeScanner

    Public Partial Class MainPage
        Inherits ContentPage

        Public Sub New()
            InitializeComponent()
        End Sub

        Private Async Sub OnSelectImage(sender As Object, e As EventArgs)
            Try
                ' Open the system file picker filtered to image types
                Dim result = Await FilePicker.PickAsync(New PickOptions With {
                    .FileTypes = FilePickerFileType.Images,
                    .PickerTitle = "Select a barcode image"
                })

                If result IsNot Nothing Then
                    ' Display the selected image in the UI
                    Dim stream = Await result.OpenReadAsync()
                    SelectedImageDisplay.Source = ImageSource.FromStream(Function() stream)

                    ' Decode all barcodes found in the image
                    Dim barcodes = BarcodeReader.Read(result.FullPath)

                    If barcodes.Count > 0 Then
                        ' Build a display string listing each barcode type and value
                        Dim output As String = String.Join(vbCrLf, barcodes.Select(Function(b) $"{b.BarcodeType}: {b.Value}"))
                        ResultsLabel.Text = output
                    Else
                        ResultsLabel.Text = "No barcodes detected in image"
                    End If
                End If
            Catch ex As Exception
                ResultsLabel.Text = $"Error: {ex.Message}"
            End Try
        End Sub
    End Class

End Namespace
$vbLabelText   $csharpLabel

BarcodeReader.Read processes the file at the given path, auto-detects all barcode symbologies present, and returns results immediately. The method supports all major 1D and 2D barcode formats including Code 128, Code 39, QR Code, Data Matrix, PDF417, and EAN-13.

.NET MAUI Scan Barcode in Windows Apps with a Powerful Barcode Scanner Library: Image 1 - Scanned barcode output

The FilePicker.PickAsync call constrains the picker to image types, so users cannot accidentally select non-image files. If result is null, the user cancelled -- the if (result != null) guard handles that silently.

How Do You Show Scan Results in a Dialog?

For brief confirmation messages, DisplayAlert provides a modal dialog without requiring additional UI elements:

private async void ShowScanSummary(BarcodeResults barcodes)
{
    if (barcodes.Count > 0)
    {
        // Inform the user how many barcodes were detected
        string message = $"Found {barcodes.Count} barcode(s) in the image.";
        await DisplayAlert("Scan Complete", message, "OK");
    }
    else
    {
        await DisplayAlert("No Results", "No barcodes were found in the image.", "OK");
    }
}
private async void ShowScanSummary(BarcodeResults barcodes)
{
    if (barcodes.Count > 0)
    {
        // Inform the user how many barcodes were detected
        string message = $"Found {barcodes.Count} barcode(s) in the image.";
        await DisplayAlert("Scan Complete", message, "OK");
    }
    else
    {
        await DisplayAlert("No Results", "No barcodes were found in the image.", "OK");
    }
}
Private Async Sub ShowScanSummary(barcodes As BarcodeResults)
    If barcodes.Count > 0 Then
        ' Inform the user how many barcodes were detected
        Dim message As String = $"Found {barcodes.Count} barcode(s) in the image."
        Await DisplayAlert("Scan Complete", message, "OK")
    Else
        Await DisplayAlert("No Results", "No barcodes were found in the image.", "OK")
    End If
End Sub
$vbLabelText   $csharpLabel

This pattern works well for simple confirmation flows. For apps that need to act on the decoded values -- for example, looking up a product by its barcode in a barcode inventory management system -- pass the barcodes collection to your business logic layer directly from the OnSelectImage handler.

How Do You Scan Multiple Barcodes and Tune Detection Speed?

When an image contains several barcodes, IronBarcode detects them all by default. For better performance when you know which formats to expect, configure BarcodeReaderOptions before calling BarcodeReader.Read:

using IronBarCode;

// Target only QR codes and Code 128 for faster detection
var options = new BarcodeReaderOptions
{
    ExpectMultipleBarcodes = true,
    ExpectBarcodeTypes     = BarcodeEncoding.QRCode | BarcodeEncoding.Code128,
    Speed                  = ReadingSpeed.Balanced
};

var barcodes = BarcodeReader.Read(imagePath, options);

foreach (var barcode in barcodes)
{
    Console.WriteLine($"Type: {barcode.BarcodeType} | Value: {barcode.Value}");
}
using IronBarCode;

// Target only QR codes and Code 128 for faster detection
var options = new BarcodeReaderOptions
{
    ExpectMultipleBarcodes = true,
    ExpectBarcodeTypes     = BarcodeEncoding.QRCode | BarcodeEncoding.Code128,
    Speed                  = ReadingSpeed.Balanced
};

var barcodes = BarcodeReader.Read(imagePath, options);

foreach (var barcode in barcodes)
{
    Console.WriteLine($"Type: {barcode.BarcodeType} | Value: {barcode.Value}");
}
Imports IronBarCode

' Target only QR codes and Code 128 for faster detection
Dim options As New BarcodeReaderOptions With {
    .ExpectMultipleBarcodes = True,
    .ExpectBarcodeTypes = BarcodeEncoding.QRCode Or BarcodeEncoding.Code128,
    .Speed = ReadingSpeed.Balanced
}

Dim barcodes = BarcodeReader.Read(imagePath, options)

For Each barcode In barcodes
    Console.WriteLine($"Type: {barcode.BarcodeType} | Value: {barcode.Value}")
Next
$vbLabelText   $csharpLabel

.NET MAUI Scan Barcode in Windows Apps with a Powerful Barcode Scanner Library: Image 2 - Output for scanning multiple barcodes

The ExpectBarcodeTypes property narrows the detection engine to the specified symbologies. Setting Speed to ReadingSpeed.Faster suits high-contrast, undistorted images. ReadingSpeed.Detailed applies additional image correction passes and handles rotation, skew, and low-resolution inputs at the cost of extra processing time.

ExpectMultipleBarcodes = true tells the reader to continue scanning after the first match rather than returning early. In single-barcode scenarios, omitting this option shaves a few milliseconds from each scan.

This configuration makes the scanner practical for a range of applications: a retail app reading product barcodes, a warehouse tool processing printed barcode labels on shipment photos, or a document workflow extracting QR codes from uploaded invoices.

How Do You Handle Challenging or Low-Quality Images?

Production images are rarely pristine. Warehouse photos taken under harsh lighting, screenshots from email clients, and scanned documents all introduce noise, compression artifacts, and geometry distortion. IronBarcode exposes ImageFilterCollection to pre-process images before decoding:

using IronBarCode;
using IronSoftware.Drawing;

// Apply corrections for a low-quality warehouse photo
var options = new BarcodeReaderOptions
{
    ImageFilters = new ImageFilterCollection
    {
        new SharpenFilter(),
        new ContrastFilter(1.2f),
        new DenoiseFilter()
    },
    Speed = ReadingSpeed.Detailed
};

var barcodes = BarcodeReader.Read(imagePath, options);
using IronBarCode;
using IronSoftware.Drawing;

// Apply corrections for a low-quality warehouse photo
var options = new BarcodeReaderOptions
{
    ImageFilters = new ImageFilterCollection
    {
        new SharpenFilter(),
        new ContrastFilter(1.2f),
        new DenoiseFilter()
    },
    Speed = ReadingSpeed.Detailed
};

var barcodes = BarcodeReader.Read(imagePath, options);
Imports IronBarCode
Imports IronSoftware.Drawing

' Apply corrections for a low-quality warehouse photo
Dim options As New BarcodeReaderOptions With {
    .ImageFilters = New ImageFilterCollection From {
        New SharpenFilter(),
        New ContrastFilter(1.2F),
        New DenoiseFilter()
    },
    .Speed = ReadingSpeed.Detailed
}

Dim barcodes = BarcodeReader.Read(imagePath, options)
$vbLabelText   $csharpLabel

SharpenFilter recovers edge definition from compressed or out-of-focus captures. ContrastFilter helps when lighting is uneven. DenoiseFilter reduces speckle from low-resolution scans. Combining these filters with ReadingSpeed.Detailed maximises read rates on difficult material.

For .NET MAUI apps that accept user-uploaded images from a variety of sources, applying a conservative filter set by default and escalating to more aggressive correction on a second retry improves the user experience without adding visible latency in the common case. You can also pass a Uri or byte[] directly to BarcodeReader.Read, which is useful when the image arrives from a network response rather than the file system. See the IronBarcode how-to guides for additional input source examples.

Why Does Image-Based Scanning Suit .NET MAUI Applications?

Live camera scanning through a CameraView control requires platform-specific permission grants, lifecycle management for the camera preview, and handling of focus events. On iOS this also means configuring AVCaptureSession; on Android, CameraX. Each platform adds its own failure modes.

Image-based scanning removes that entire category of concern. The IronBarcode API reference shows that BarcodeReader.Read accepts a file path, a Stream, a Bitmap, or a byte[] -- any representation your MAUI app can produce. This means the same scanning logic works whether the image came from FilePicker, a network download, a PDF page rendered to bitmap, or an email attachment.

Battery consumption is lower because the camera hardware stays off. There is no UI flicker from a live preview, and no need to manage camera lifecycle events across app suspend and resume. On tablet and desktop form factors -- where a live camera viewfinder is rarely appropriate -- image-based decoding is the natural default rather than a compromise. Users can open files from cloud storage, local folders, or camera rolls with the same FilePicker call regardless of device type.

For workflows where users photograph barcode labels and upload them -- common in ASP.NET barcode scanner web applications -- the same BarcodeReader.Read call works on both the mobile client and the server, removing the need to maintain two scanning implementations.

How Does IronBarcode Compare to ZXing.Net.MAUI?

ZXing.Net.MAUI targets live camera scanning and works well when real-time viewfinder feedback is a product requirement. It requires CameraView integration, platform handler registration, and runtime permission requests.

IronBarcode targets file-based and stream-based decoding, which covers the majority of enterprise document workflows. It supports a broader range of symbologies including PDF417, Data Matrix, and Code 128, and provides image filter pre-processing that ZXing does not expose. For applications where users capture or upload images rather than scan items live, IronBarcode is the more direct fit.

If your app needs live camera scanning in addition to file-based decoding, you can combine both libraries: ZXing.Net.MAUI for the viewfinder workflow and IronBarcode for batch file processing.

What Are Your Next Steps?

Building a .NET MAUI barcode scanner with IronBarcode takes less than 30 lines of C# code. The image file approach keeps your MAUI codebase free of camera permission logic and platform-specific initialization, and the same scanning call runs identically on Windows, Android, and iOS.

The IronBarcode API documentation covers additional capabilities: reading barcodes from PDF documents, batch processing multiple images, writing custom image filters, and generating barcodes alongside reading them. The features overview lists every supported symbology and format.

Start a free trial to test IronBarcode in your project, or purchase a license when ready for production deployment.

Get stated with IronBarcode now.
green arrow pointer

Frequently Asked Questions

How do you create a barcode scanner in .NET MAUI without a camera?

Install IronBarcode via NuGet (`Install-Package BarCode`), then call `BarcodeReader.Read(filePath)` with a path obtained from `FilePicker.PickAsync`. No camera permissions or `CameraView` setup are needed.

Can IronBarcode scan barcodes on Android and iOS in .NET MAUI?

Yes. The same `BarcodeReader.Read` call runs on Windows, Android, and iOS without any platform-specific code paths or manifest changes.

What image formats does IronBarcode support for barcode scanning?

IronBarcode reads barcodes from JPEG, PNG, GIF, TIFF, and BMP files. It also accepts `Stream`, `Bitmap`, and `byte[]` inputs, so images from network responses work without writing to disk first.

How do you scan multiple barcodes from a single image in .NET MAUI?

Set `ExpectMultipleBarcodes = true` in `BarcodeReaderOptions` and pass the options to `BarcodeReader.Read`. The reader returns all detected barcodes in a single `BarcodeResults` collection.

What is the difference between IronBarcode and ZXing.Net.MAUI?

ZXing.Net.MAUI targets live camera scanning via a `CameraView` control. IronBarcode targets file-based and stream-based decoding, supports more symbologies (including PDF417 and Data Matrix), and provides image filter pre-processing for low-quality inputs.

How do you improve barcode detection on blurry or low-quality images?

Add an `ImageFilterCollection` to `BarcodeReaderOptions` with `SharpenFilter`, `ContrastFilter`, and `DenoiseFilter`, and set `Speed = ReadingSpeed.Detailed`. This applies image corrections before the decode pass.

What barcode formats does IronBarcode support in .NET MAUI?

IronBarcode supports all major 1D and 2D symbologies: Code 128, Code 39, QR Code, Data Matrix, PDF417, EAN-13, EAN-8, UPC-A, UPC-E, Aztec, and more. The full list is on the IronBarcode features page.

Jordi Bardia
Software Engineer
Jordi is most proficient in Python, C# and C++, when he isn’t leveraging his skills at Iron Software; he’s game programming. Sharing responsibilities for product testing, product development and research, Jordi adds immense value to continual product improvement. The varied experience keeps him challenged and engaged, and he ...
Read More

Iron Support Team

We're online 24 hours, 5 days a week.
Chat
Email
Call Me