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.
Install the Dependency
Install Dynamsoft Barcode Reader via 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
-
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>
-
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(); }
Read Barcodes from Camera Preview
-
Open a
MainWindow.xaml
. Add aLiveScan
button and aMediaPlayerElement
. TheMediaPlayerElement
is used as the container for the camera preview. It is in theCameraPanel
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>
-
Start the camera when the
LiveScan
button is clicked. We can get the camera frame in theFrameArrived
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; }
-
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