Blazor is a powerful framework by Microsoft that enables developers to build interactive web applications using C# instead of JavaScript, bringing the full power of .NET to the browser. While Blazor allows you to write most of your web app in C#, calling existing JavaScript APIs remains an inevitable part of enhancing Blazor functionalities, especially when integrating with powerful libraries like the Dynamsoft JavaScript Barcode SDK. In this tutorial, we will show you how to build a .NET Blazor application for scanning 1D/2D barcodes by leveraging the interop between C# and JavaScript.
Blazor Barcode Scanner Demo Video
Online Demo
https://yushulx.me/blazor-barcode-mrz-document-scanner/
Prerequisites
-
Dynamsoft Capture Vision Bundle: This bundle includes all Dynamsoft JavaScript imaging SDKs, designed to work seamlessly together. It is available on npm.
Dynamsoft Capture Vision Trial License: A trial license supports all features (including Barcode, MRZ, Document recognition, and more) of the Dynamsoft Capture Vision Bundle for 30 days. You can apply for a trial license here.
Step 1: Integrate Dynamsoft Capture Vision Bundle into a Blazor WebAssembly Project
- Create a Blazor WebAssembly Project: Start a new Blazor WebAssembly project in Visual Studio or Visual Studio Code.
-
Add the Capture Vision Bundle Script: In the
wwwroot/index.html
file, include the Dynamsoft Capture Vision Bundle script:
<script src="https://cdn.jsdelivr.net/npm/dynamsoft-capture-vision-bundle"></script>
-
Create a JavaScript File for Interop: Create a file named
jsInterop.js
in thewwwroot
folder and include it in theindex.html
:
<script src="jsInterop.js"></script>
-
Configure JavaScript to Set the License: In
jsInterop.js
, define asetLicense
function that specifies the resource paths for the dependencies and sets the license key:
window.jsFunctions = { setLicense: async function setLicense(license) { try { Dynamsoft.Core.CoreModule.engineResourcePaths = { std: "https://cdn.jsdelivr.net/npm/dynamsoft-capture-vision-std@1.2.10/dist/", dip: "https://cdn.jsdelivr.net/npm/dynamsoft-image-processing@2.2.30/dist/", core: "https://cdn.jsdelivr.net/npm/dynamsoft-core@3.2.30/dist/", license: "https://cdn.jsdelivr.net/npm/dynamsoft-license@3.2.21/dist/", cvr: "https://cdn.jsdelivr.net/npm/dynamsoft-capture-vision-router@2.2.30/dist/", dce: "https://cdn.jsdelivr.net/npm/dynamsoft-camera-enhancer@4.0.3/dist/", dbr: "https://cdn.jsdelivr.net/npm/dynamsoft-barcode-reader@10.2.10/dist/", dlr: "https://cdn.jsdelivr.net/npm/dynamsoft-label-recognizer@3.2.30/dist/", dcp: "https://cdn.jsdelivr.net/npm/dynamsoft-code-parser@2.2.10/dist/", ddn: "https://cdn.jsdelivr.net/npm/dynamsoft-document-normalizer@2.2.10/dist/", }; Dynamsoft.Core.CoreModule.loadWasm(["dbr"]); Dynamsoft.License.LicenseManager.initLicense(license, true); await initSDK(); } catch (e) { console.log(e); return false; } return true; } };
The
loadWasm
method preloads the WebAssembly module for the Dynamsoft Barcode Reader SDK. If you don't load it ahead of time, the SDK will load it on-demand, which may cause a delay in the first barcode scanning. -
Update the Blazor Page to Activate the SDK: Modify the
Pages/Home.razor
file to include HTML and C# code that allows users to activate the SDK with a valid license key:
@page "/" @inject IJSRuntime JSRuntime <PageTitle>Home</PageTitle> <p>Click <a href="https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform" target="_blank">here</a> to obtain a Dynamsoft Capture Vision Trial License.</p> <EditForm Model="@this"> <InputText @bind-Value="LicenseKey" placeholder="Enter your license key" /> <button type="button" class="btn btn-primary" @onclick="SetLicenseKey">Activate the SDK</button> </EditForm> @code { Boolean initialized = false; private string LicenseKey = "LICENSE-KEY"; private async Task SetLicenseKey() { initialized = await JSRuntime.InvokeAsync<Boolean>("jsFunctions.setLicense", LicenseKey); StateHasChanged(); } }
Step 2: Implement a Barcode Reader Razor Page
-
Create the Barcode Reader Component: Create a new Razor component called
Reader.razor
in the Pages folder and add it to your navigation menu inNavMenu.razor
:
<div class="nav-item px-3"> <NavLink class="nav-link" href="barcodereader"> <span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Barcode Reader </NavLink> </div>
Ensure that the
href
attribute value (barcodereader
) matches the path specified in the Razor component. -
Implement the Barcode Reader in Reader.razor: Add the following code in
Reader.razor
to set up the barcode reader functionality:
@page "/barcodereader" @inject IJSRuntime JSRuntime <button @onclick="ReadBarcodes">Select an image</button> <p class="p-result">@result</p> <div id="imageview"> <img id="@imageId" /> <canvas id="@overlayId"></canvas> </div> @code { private String result = ""; private DotNetObjectReference<Reader>? objRef; private String imageId = "image"; private String overlayId = "overlay"; protected override void OnInitialized() { objRef = DotNetObjectReference.Create(this); } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { // Call the JavaScript function when the page is loaded await JSRuntime.InvokeVoidAsync("jsFunctions.initReader"); } } public async Task ReadBarcodes() { await JSRuntime.InvokeVoidAsync( "jsFunctions.selectFile", objRef, overlayId, imageId); } [JSInvokable] public void ReturnBarcodeResultsAsync(String text) { result = text; StateHasChanged(); } public void Dispose() { objRef?.Dispose(); } }
Explanation
-
@page "/barcodereader"
sets the route for the Razor component. -
OnInitialized()
creates aDotNetObjectReference
, enablingJavaScript
to callC#
methods. -
OnAfterRenderAsync()
triggers the JavaScript functioninitReader
when the page loads. -
ReadBarcodes()
invokes the JavaScript functionselectFile
, which opens a file dialog to select an image for barcode scanning. -
ReturnBarcodeResultsAsync()
is a C# method that JavaScript calls to return barcode results to the Blazor component.
-
-
Add JavaScript Functions in jsInterop.js: Add the corresponding JavaScript functions in
jsInterop.js
:
function showResults(result, dotnetRef) { clearOverlay(); let txts = []; try { let localization; let items = result.items if (items.length > 0) { for (var i = 0; i < items.length; ++i) { if (items[i].type !== Dynamsoft.Core.EnumCapturedResultItemType.CRIT_BARCODE) { continue; } let item = items[i]; txts.push(item.text); localization = item.location; drawOverlay( localization, item.text ); } } } catch (e) { alert(e); } let barcoderesults = txts.join(', '); if (txts.length == 0) { barcoderesults = 'No barcode found'; } if (dotnetRef) { dotnetRef.invokeMethodAsync('ReturnBarcodeResultsAsync', barcoderesults); } } function decodeImage(dotnetRef, url, data) { const img = new Image() img.onload = () => { updateOverlay(img.width, img.height); if (cvr) { cvr.capture(url, 'ReadBarcodes_Balance').then((result) => { showResults(result, dotnetRef); }); } } img.src = url } window.jsFunctions = { ... initReader: async function () { try { dispose(); cvr = await Dynamsoft.CVR.CaptureVisionRouter.createInstance(); } catch (e) { console.log(e); } }, selectFile: async function (dotnetRef, overlayId, imageId) { if (cameraEnhancer) { cameraEnhancer.dispose(); cameraEnhancer = null; } initOverlay(document.getElementById(overlayId)); if (cvr) { let input = document.createElement("input"); input.type = "file"; input.onchange = async function () { try { let file = input.files[0]; var fr = new FileReader(); fr.onload = function () { let image = document.getElementById(imageId); image.src = fr.result; image.style.display = 'block'; decodeImage(dotnetRef, fr.result, file); } fr.readAsDataURL(file); } catch (ex) { alert(ex.message); throw ex; } }; input.click(); } else { alert("The barcode reader is still initializing."); } }, };
Explanation
-
initReader()
initializes the Capture Vision Router. -
decodeImage()
decodes barcodes from the selected image. -
showResults()
displays the decoded barcode results. -
selectFile()
allows users to select an image file, which is then processed for barcode scanning.
-
Step 3: Implement a Barcode Scanner Razor Page
-
Create the Barcode Scanner Component: Create a new Razor component called
Scanner.razor
in the Pages folder and add it to the navigation menu inNavMenu.razor
:
<div class="nav-item px-3"> <NavLink class="nav-link" href="barcodescanner"> <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Barcode Scanner </NavLink> </div>
-
Implement the Barcode Scanner in Scanner.razor: Add the following code in
Scanner.razor
to set up the barcode scanner functionality:
@page "/barcodescanner" @inject IJSRuntime JSRuntime <div class="select"> <button @onclick="GetCameras">Get Cameras</button> <label for="videoSource"></label> <select id="@videoSourceId"></select> <button @onclick="StartCamera">Open Camera</button> <button @onclick="StopCamera">Stop Camera</button> </div> <div id="videoview"> <div class="dce-video-container" id="@videoContainerId"></div> <canvas id="@overlayId"></canvas> </div> @code { private String result = ""; private DotNetObjectReference<Scanner>? objRef; private string videoSourceId = "videoSource"; private string overlayId = "overlay"; private string videoContainerId = "videoContainer"; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { objRef = DotNetObjectReference.Create(this); await JSRuntime.InvokeAsync<Boolean>("jsFunctions.initScanner", objRef, videoContainerId, videoSourceId, overlayId); } } [JSInvokable] public void ReturnBarcodeResultsAsync(String text) { result = text; StateHasChanged(); } public void Dispose() { objRef?.Dispose(); } public async Task GetCameras() { await JSRuntime.InvokeVoidAsync("jsFunctions.getCameras"); } public async Task StartCamera() { await JSRuntime.InvokeVoidAsync("jsFunctions.startCamera"); } public async Task StopCamera() { await JSRuntime.InvokeVoidAsync("jsFunctions.stopCamera"); } }
Explanation
-
@page "/barcodescanner"
defines the route for the Razor component. -
OnAfterRenderAsync()
initializes the barcode scanner by calling the JavaScript functioninitScanner
once the page is loaded. -
GetCameras()
,StartCamera()
, andStopCamera()
are methods that interact with JavaScript functions to manage the camera and barcode scanning operations.
-
-
Add JavaScript Functions in jsInterop.js: Add the corresponding JavaScript functions in your
jsInterop.js
file:
async function openCamera() { clearOverlay(); try { let deviceId = videoSelect.value; if (cameraEnhancer && deviceId !== "") { await cameraEnhancer.selectCamera(deviceId); await cameraEnhancer.open(); cvr.startCapturing('ReadSingleBarcode'); } } catch(e) { console.log(e); } } window.jsFunctions = { ... initScanner: async function (dotnetRef, videoId, selectId, overlayId) { let canvas = document.getElementById(overlayId); initOverlay(canvas); videoSelect = document.getElementById(selectId); videoSelect.onchange = openCamera; try { dispose(); let cameraView = await Dynamsoft.DCE.CameraView.createInstance(); cameraEnhancer = await Dynamsoft.DCE.CameraEnhancer.createInstance(cameraView); let uiElement = document.getElementById(videoId); uiElement.append(cameraView.getUIElement()); cameraView.getUIElement().shadowRoot?.querySelector('.dce-sel-camera')?.setAttribute('style', 'display: none'); cameraView.getUIElement().shadowRoot?.querySelector('.dce-sel-resolution')?.setAttribute('style', 'display: none'); cvr = await Dynamsoft.CVR.CaptureVisionRouter.createInstance(); cvr.setInput(cameraEnhancer); cvr.addResultReceiver({ onCapturedResultReceived: (result) => { showResults(result, dotnetRef); }, }); cvr.addResultReceiver({ onDecodedBarcodesReceived: (result) => { if (!result.barcodeResultItems.length) return; }, }); cameraEnhancer.on('played', () => { updateResolution(); }); } catch (e) { console.log(e); result = false; } return true; }, getCameras: async function () { if (cameraEnhancer) { let cameras = await cameraEnhancer.getAllCameras(); listCameras(cameras); } }, startCamera: async function() { openCamera(); }, stopCamera: async function () { try { if (cameraEnhancer) { cameraEnhancer.pause(); } } catch (e) { console.log(e); } } };
Explanation
-
initScanner()
initializes the camera view, camera enhancer, and Capture Vision Router, and registers callback functions to handle barcode results. -
getCameras()
retrieves the list of available cameras. -
startCamera()
opens the selected camera and starts barcode scanning. -
stopCamera()
stops the camera preview, halting any ongoing image processing.
-
Step 4: Run the Blazor Barcode Reader and Scanner Application
To launch your Blazor application, press F5
in Visual Studio or use your preferred method to start debugging. Once the application is running, you can access the barcode reader and scanner pages to scan barcodes and QR codes effortlessly.
-
Barcode Reader: Use the barcode reader page to select image files for barcode detection.
-
Barcode Scanner: The barcode scanner page allows for real-time scanning using your device's camera, making it easy to detect barcodes and QR codes.
Source Code
https://github.com/yushulx/blazor-barcode-mrz-document-scanner