How to Build a Barcode Scanner in WinUI 3

Web and mobile app development has drawn most of the attention in recent years, but desktop apps still play a critical role in our productivity.

The Windows UI Library (WinUI) is a native user experience (UX) framework for both Windows desktop and UWP applications. It is Microsoft’s latest technology to build desktop apps. WinUI comes in two versions. WinUI 3 can be used to build production-ready desktop/Win32 Windows apps while WinUI 2 is mainly used by UWP apps.

In this article, we are going to build a WinUI 3 barcode scanner with Dynamsoft Barcode Reader. It can be used on devices like rugged tablets to perform everyday tasks involving barcodes.

Getting started with Dynamsoft Barcode Reader

New Project

Open Visual Studio 2022 and create a new WinUI 3 project.

New project

Install the Dependency

Install Dynamsoft Barcode Reader via Nuget.

Nuget

Initialize Dynamsoft Barcode Reader

Open MainWindow.xaml.cs and initialize an instance of Dynamsoft Barcode Reader.

public sealed partial class MainWindow : Window
{
    public BarcodeReader Reader { get; set; }
    public MainWindow()
    {
        this.InitializeComponent();
        this.InitBarcodeReader();
    }
    
    private void InitBarcodeReader() {
        string errorMsg;
        EnumErrorCode errorCode = BarcodeReader.InitLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ==", out errorMsg); //using a one-day trial license
        if (errorCode != EnumErrorCode.DBR_SUCCESS)
        {
            // Add your code for license error processing;
            System.Diagnostics.Debug.WriteLine(errorMsg);
        }
        Reader = new BarcodeReader();
    }
}

You need a license to use Dynamsoft Barcode Reader. You can apply for one here.

Read Barcodes from Image or PDF Files

  1. Open MainWindow.xaml and add a button and a text block.

    <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button x:Name="PickImageButton" Click="PickImageButton_Click">Pick an image to read barcodes</Button>
        <TextBlock x:Name="DecodingResultsTextBox" />
    </StackPanel>
    
  2. If the PickImageButton is clicked, show a file selection dialog, read barcodes from that file and display the barcode results. Note that we need to use Interop to call WinRT’s Picker API from a WinUI 3 app (learn more about it here).

    private async void PickImageButton_Click(object sender, RoutedEventArgs e)
    {
        var picker = new Windows.Storage.Pickers.FileOpenPicker();
        // Get the current window's HWND by passing in the Window object
        var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
    
        // Associate the HWND with the file picker
        WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
    
        picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
        picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary;
        picker.FileTypeFilter.Add(".jpg");
        picker.FileTypeFilter.Add(".jpeg");
        picker.FileTypeFilter.Add(".png");
        picker.FileTypeFilter.Add(".bmp");
        picker.FileTypeFilter.Add(".pdf");
    
        Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
        if (file != null) {
            TextResult[] results = Reader.DecodeFile(file.Path, "");
            DecodingResultsTextBox.Text = BuildResultsString(results);
        }
    }
    
    private string BuildResultsString(TextResult[] results) {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine("Found " + results.Length
            .ToString() + " result(s).");
        for (int i = 0; i < results.Length; i++)
        {
            TextResult result = results[i];
            sb.Append(i + 1);
            sb.Append(". ");
            sb.Append(result.BarcodeFormatString);
            sb.Append(": ");
            sb.Append(result.BarcodeText);
            sb.Append('\n');
        }
        return sb.ToString();
    }
    

Image decoding

Read Barcodes from Camera Preview

  1. Open a MainWindow.xaml. Add a LiveScan button and a MediaPlayerElement. The MediaPlayerElement is used as the container for the camera preview. It is in the CameraPanel which is hidden by default and will be displayed when the camera is open.

    <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
        <StackPanel x:Name="DefaultPanel" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
            <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
                <Button x:Name="PickImageButton" Click="PickImageButton_Click">Pick an image to read barcodes</Button>
                <Button x:Name="LiveScanButton" Click="LiveScanButton_Click">Live scan</Button>
            </StackPanel>
            <TextBlock x:Name="DecodingResultsTextBox" />
        </StackPanel>
        <StackPanel x:Name="CameraPanel" Visibility="Collapsed" >
            <MediaPlayerElement
            x:Name="player"
            AutoPlay="True" />
        </StackPanel>
    </StackPanel>
    
  2. Start the camera when the LiveScan button is clicked. We can get the camera frame in the FrameArrived event.

    private MediaCapture _capture;
    private MediaFrameReader _frameReader;
    private MediaSource _mediaSource;
    private async void LiveScanButton_Click(object sender, RoutedEventArgs e) {
        ToggleCameraPanel(true);
        await InitializeCaptureAsync();
    }
       
    private void ToggleCameraPanel(bool on) 
    {
        CameraPanel.Visibility = on ? Visibility.Visible: Visibility.Collapsed;
        DefaultPanel.Visibility = on ? Visibility.Collapsed : Visibility.Visible;
    }
       
    //https://stackoverflow.com/questions/76956862/how-to-scan-a-qr-code-in-winui-3-using-webcam
    private async Task InitializeCaptureAsync()
    {
        // get the first capture device (change this if you want)
        var sourceGroup = (await MediaFrameSourceGroup.FindAllAsync())?.FirstOrDefault();
        if (sourceGroup == null)
            return; // not found!
    
        // init capture & initialize
        _capture = new MediaCapture();
        await _capture.InitializeAsync(new MediaCaptureInitializationSettings
        {
            SourceGroup = sourceGroup,
            SharingMode = MediaCaptureSharingMode.SharedReadOnly,
            MemoryPreference = MediaCaptureMemoryPreference.Cpu, // to ensure we get SoftwareBitmaps
        });
    
        // initialize source
        var source = _capture.FrameSources[sourceGroup.SourceInfos[0].Id];
    
        // create a reader to get frames & pass the reader to player to visualize the webcam
        _frameReader = await _capture.CreateFrameReaderAsync(source, MediaEncodingSubtypes.Bgra8);
        _frameReader.FrameArrived += OnFrameArrived;
        await _frameReader.StartAsync();
    
        _mediaSource = MediaSource.CreateFromMediaFrameSource(source);
        player.Source = _mediaSource;
    }
       
    private async void OnFrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
    {
        var bmp = sender.TryAcquireLatestFrame()?.VideoMediaFrame?.SoftwareBitmap;
        if (bmp == null)
            return;
    }
    
  3. Use Dynamsoft Barcode Reader to read barcodes from the camera frame. If a barcode is found, stop the camera and display the barcode results.

    private bool decoding = false; //use this property to avoid decoding several frames at the same time.
    private async void OnFrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
    {
        var bmp = sender.TryAcquireLatestFrame()?.VideoMediaFrame?.SoftwareBitmap;
        if (bmp == null)
            return;
        if (decoding == true) {
            return;
        }
        decoding = true;
        using (var stream = new InMemoryRandomAccessStream())
        {
            BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
            encoder.SetSoftwareBitmap(bmp);
            await encoder.FlushAsync();
            var bytes = new byte[stream.Size];
            await stream.ReadAsync(bytes.AsBuffer(), (uint)stream.Size, InputStreamOptions.None);
            TextResult[] results = Reader.DecodeFileInMemory(bytes, "");
            System.Diagnostics.Debug.WriteLine(results.Length);
            if (results.Length > 0)
            {
                DispatcherQueue.TryEnqueue(async () => { 
                    DecodingResultsTextBox.Text = BuildResultsString(results);
                    await TerminateCaptureAsync();
                    ToggleCameraPanel(false);
                });
                   
            }
        }
        decoding = false;
    }
    
    private async Task TerminateCaptureAsync()
    {
        player.Source = null;
    
        _mediaSource?.Dispose();
        _mediaSource = null;
    
        if (_frameReader != null)
        {
            _frameReader.FrameArrived -= OnFrameArrived;
            await _frameReader.StopAsync();
            _frameReader?.Dispose();
            _frameReader = null;
        }
    
        _capture?.Dispose();
        _capture = null;
    }
    

Demo video:

Source Code

The source code of the project is available here: https://github.com/tony-xlh/WinUI-Barcode-Reader