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.
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