How to Build a WinUI 3 Barcode Scanner in C# (Image, PDF, and Webcam)
You can build a WinUI 3 barcode scanner in C# by installing the Dynamsoft.DotNet.BarcodeReader.Bundle NuGet package and calling its CaptureVisionRouter API: CaptureMultiPages() decodes image and multi-page PDF files picked by the user, and Capture() decodes webcam frame bytes in memory. One license activation and a few dozen lines of code are all it takes.
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.

What you’ll build: A WinUI 3 desktop app that reads barcodes three ways — from a picked image file, from a (multi-page) PDF document, and from a live webcam preview. The app uses CaptureVisionRouter from Dynamsoft.DotNet.BarcodeReader.Bundle (v11.x) and shows each barcode’s format and text in the window.
Key Takeaways
- The current .NET package for Dynamsoft Barcode Reader is
Dynamsoft.DotNet.BarcodeReader.Bundle(v11.x). Since version 10, the SDK is built on the Dynamsoft Capture Vision architecture and exposes barcode reading throughCaptureVisionRouterinstead of the legacyBarcodeReaderclass. CaptureVisionRouter.CaptureMultiPages(path, PresetTemplate.PT_READ_BARCODES)decodes barcodes from image files and multi-page PDF documents in one call; results are read fromGetDecodedBarcodesResult()asBarcodeResultItemobjects.CaptureVisionRouter.Capture(bytes, PresetTemplate.PT_READ_BARCODES)decodes barcodes from image bytes in memory, which is how webcam frames fromMediaCaptureare processed without saving files to disk.- In a WinUI 3 desktop app, WinRT’s file picker must be initialized with the window’s HWND via
WinRT.Interop.WindowNative.GetWindowHandle()andInitializeWithWindow. - A valid license key is required; initialize it once with
LicenseManager.InitLicense()before creating the router.
Common Developer Questions
How do I read barcodes from an image or PDF file in a WinUI 3 app?
Install the Dynamsoft.DotNet.BarcodeReader.Bundle NuGet package, initialize a license with LicenseManager.InitLicense(), then call CaptureVisionRouter.CaptureMultiPages(filePath, PresetTemplate.PT_READ_BARCODES). The method returns one CapturedResult per page; call GetDecodedBarcodesResult() on each result and read BarcodeResultItem.GetFormatString() and GetText() for every barcode found. In a WinUI 3 desktop app, remember to associate the file picker’s HWND with your window using WinRT.Interop, otherwise the picker will not open.
How do I scan barcodes from the webcam in WinUI 3?
Use MediaCapture with a MediaFrameReader to receive preview frames in the FrameArrived event, encode each SoftwareBitmap to PNG bytes with BitmapEncoder, and pass the bytes to CaptureVisionRouter.Capture(bytes, PresetTemplate.PT_READ_BARCODES). Set MemoryPreference to Cpu in MediaCaptureInitializationSettings so that SoftwareBitmap frames are available, and guard the decoding loop with a flag so only one frame is decoded at a time.
Which NuGet package should I use for barcode reading in a .NET desktop app?
Use Dynamsoft.DotNet.BarcodeReader.Bundle (v11.x). The older Dynamsoft.DotNet.Barcode package (v9.x) is deprecated; the new bundle uses the CaptureVisionRouter API with PresetTemplate.PT_READ_BARCODES and requires a new license key.
Do I need a license to run this sample?
Yes. Dynamsoft Barcode Reader requires a license, which you activate once in code via LicenseManager.InitLicense(key, out errorMsg). The code checks the returned error code against EnumErrorCode.EC_OK and EC_LICENSE_WARNING — see the Prerequisites section for how to get a trial license.
Prerequisites
- Visual Studio 2022 with the Windows App SDK workload, so the WinUI 3 project templates are available.
- .NET 6 SDK or later; the sample targets
net6.0-windows10.0.19041.0. - A Dynamsoft Barcode Reader license key — Get a 30-day free trial license.
Step 1: Create a New WinUI 3 Project
Open Visual Studio 2022 and create a new WinUI 3 project.

Step 2: Install the Barcode Reader NuGet Package
Install Dynamsoft Barcode Reader via NuGet. Since version 10, the SDK is built on the Dynamsoft Capture Vision architecture and the .NET package is Dynamsoft.DotNet.BarcodeReader.Bundle. It exposes the barcode reading functionality through the CaptureVisionRouter API.

Step 3: Initialize CaptureVisionRouter with a License
Open MainWindow.xaml.cs and initialize an instance of CaptureVisionRouter. The following namespaces are required:
using Dynamsoft.Core;
using Dynamsoft.CVR;
using Dynamsoft.DBR;
using Dynamsoft.License;
public sealed partial class MainWindow : Window
{
public CaptureVisionRouter Router { get; set; }
public MainWindow()
{
this.InitializeComponent();
this.InitBarcodeReader();
}
private void InitBarcodeReader() {
string errorMsg;
int errorCode = LicenseManager.InitLicense("LICENSE-KEY", out errorMsg); //using a one-day trial license
if (errorCode != (int)EnumErrorCode.EC_OK && errorCode != (int)EnumErrorCode.EC_LICENSE_WARNING)
{
// Add your code for license error processing;
System.Diagnostics.Debug.WriteLine(errorMsg);
}
Router = new CaptureVisionRouter();
}
}
A valid license key is required to use Dynamsoft Barcode Reader — get one as described in the Prerequisites section.
Step 4: Read Barcodes from Image or PDF Files
-
Open
MainWindow.xamland 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
PickImageButtonis 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) { List<BarcodeResultItem> results = DecodeFile(file.Path); DecodingResultsTextBox.Text = BuildResultsString(results); } } // Decode an image or a (multi-page) PDF file private List<BarcodeResultItem> DecodeFile(string path) { List<BarcodeResultItem> allItems = new List<BarcodeResultItem>(); CapturedResult[] results = Router.CaptureMultiPages(path, PresetTemplate.PT_READ_BARCODES); if (results != null) { foreach (CapturedResult result in results) { DecodedBarcodesResult barcodesResult = result.GetDecodedBarcodesResult(); if (barcodesResult != null) { allItems.AddRange(barcodesResult.GetItems()); } } } return allItems; } private string BuildResultsString(List<BarcodeResultItem> results) { StringBuilder sb = new StringBuilder(); sb.AppendLine("Found " + results.Count .ToString() + " result(s)."); for (int i = 0; i < results.Count; i++) { BarcodeResultItem result = results[i]; sb.Append(i + 1); sb.Append(". "); sb.Append(result.GetFormatString()); sb.Append(": "); sb.Append(result.GetText()); sb.Append('\n'); } return sb.ToString(); }

Step 5: Read Barcodes from the Webcam Preview
-
Open a
MainWindow.xaml. Add aLiveScanbutton and aMediaPlayerElement. TheMediaPlayerElementis used as the container for the camera preview. It is in theCameraPanelwhich 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
LiveScanbutton is clicked. We can get the camera frame in theFrameArrivedevent.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); List<BarcodeResultItem> results = DecodeBytes(bytes); System.Diagnostics.Debug.WriteLine(results.Count); if (results.Count > 0) { DispatcherQueue.TryEnqueue(async () => { DecodingResultsTextBox.Text = BuildResultsString(results); await TerminateCaptureAsync(); ToggleCameraPanel(false); }); } } decoding = false; } // Decode barcode(s) from image bytes in memory (e.g. a camera frame) private List<BarcodeResultItem> DecodeBytes(byte[] bytes) { List<BarcodeResultItem> allItems = new List<BarcodeResultItem>(); CapturedResult result = Router.Capture(bytes, PresetTemplate.PT_READ_BARCODES); DecodedBarcodesResult barcodesResult = result?.GetDecodedBarcodesResult(); if (barcodesResult != null) { allItems.AddRange(barcodesResult.GetItems()); } return allItems; } 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:
Common Issues & Edge Cases
- The file picker never opens or throws an exception. In a WinUI 3 desktop (Win32) app, WinRT’s
FileOpenPickermust be associated with the window’s HWND viaWinRT.Interop.WindowNative.GetWindowHandle(this)andInitializeWithWindow.Initialize(picker, hwnd). Skipping this step is the most common cause of picker failures. - No barcode is ever decoded from the camera. Make sure
MediaCaptureInitializationSettings.MemoryPreferenceis set toCpu; otherwiseVideoMediaFrame.SoftwareBitmapcan be null and the frame is skipped. Also keep thedecodingflag so that only one frame is decoded at a time — concurrentCapture()calls fromFrameArrivedcan starve the UI thread. - Decoding returns zero results for everything. Check the return code of
LicenseManager.InitLicense(). The public trial license requires a network connection for activation, and a key issued for the old v9Dynamsoft.DotNet.Barcodepackage does not work with the v11Dynamsoft.DotNet.BarcodeReader.Bundlepackage. - UI updates from the camera callback do not appear.
FrameArrivedis raised on a worker thread; marshal result updates back withDispatcherQueue.TryEnqueue()as shown in Step 5.
Source Code
The source code of the project is available here: Get the complete sample project source code on GitHub