How To Create a Blazor QR Code Scanner

Introduction

This article explores the integration of a Quick Response code scanner (QR code scanner) in a Blazor application using IronQR, a .NET library. A QR code is a two-dimensional barcode that stores much more data than a regular one-dimensional barcode.

Blazor, a Microsoft framework, allows developers to either produce single-page apps (using the Blazor WebAssembly app) or to use C# for building interactive web interfaces, (Blazor Server, which we will be focusing on in this guide).

The integration of IronQR with Blazor Server for QR code scanning is a strategic combination that leverages both technologies' strengths. By integrating IronQR with Blazor applications, you can efficiently handle the generation and scanning of QR codes. This functionality of a QR code reader is increasingly in demand in various business contexts like inventory management, ticketing systems, and contactless information sharing.

Understanding the Basics

What is a Blazor Server?

Blazor Server is a web application framework that is part of the ASP.NET Core platform. It enables developers to build interactive web UIs using C# instead of JavaScript. This server-side model operates by handling user interactions over a SignalR connection, a real-time web functionality. This helps developers to create effective and interactive web apps.

Introduction to IronQR

IronQR is a .NET library that stands out for its ability to read, interpret, and generate QR codes with high accuracy. It offers a range of features, including the ability to handle different types of QR code content. IronQR's strength lies in its simplicity and ease of integration into .NET applications, making it a go-to choice for developers looking to incorporate and create QR code functionality.

How To Create a Blazor QR Code Scanner

  1. Create a Blazor Server Application in Visual Studio Code
  2. Install the QR Code Class Library using NuGet Package Manager
  3. Create the user interface using HTML and CSS in the index.razor
  4. Write the uploaded file-handling logic
  5. Write the QR Scanning logic using the QR library
  6. Display the result in the text box

Setting Up the Environment

Create a New Blazor Server Application

Start Visual Studio and choose "Create a new project." In the project template selection screen, find and select the "Blazor Server App" template. Click Next.

How To Create a Blazor QR Code Scanner: Figure 1 - Finding the correct template to implement

Having chosen the template, enter a Project Name and Location (keep everything else as default values) and click the Next button.

How To Create a Blazor QR Code Scanner: Figure 2 - Configuring the project details

Now select the desired .NET Framework and hit the create button. It will create a Blazor Server application.

How To Create a Blazor QR Code Scanner: Figure 3 - Selecting the .NET Framework and creating the project

Installing IronQR Library

Click on the Tools from the menu bar. From the dropdown menu, select the NuGet Package Manager. From the context menu, select “Manage NuGet Packages for Solution”. This will open the NuGet Package Manager tab.

How To Create a Blazor QR Code Scanner: Figure 4 - Accessing the NuGet package manager

In the NuGet Package Manager, search for “IronQR” within the "Browse" tab. Then locate the “IronQR” package in the list. Click on the “Install” button.

How To Create a Blazor QR Code Scanner: Figure 5 - Installing the IronQR package through the Browse tab

Now that you have everything installed, we can go through the project structure and how to implement everything into your project.

Implementing QR Code Scanning

Building the User Interface

The user interface for the QR Code Scanner is primarily built within the Index.razor file. This file, part of the Blazor Server project, uses a combination of HTML and Razor Syntax to create a dynamic and interactive web page. The structure includes:

@page "/"
@using System.IO
@using Microsoft.AspNetCore.Components.Forms
@using IronQr
@using IronSoftware.Drawing
@inject IJSRuntime JSRuntime
<PageTitle>QR Code Scanner</PageTitle>
<div>
    <h1>QR Code Scanner</h1> 
    <InputFile OnChange="HandleSelectedFile" accept="image/*" class="file-input" />
    @if (!string.IsNullOrEmpty(qrImageSrc))
    {
        <img src="@qrImageSrcForDisplay" alt="QR Code Image" class="qr-image" />
    }
    <button @onclick="ScanQRCode" disabled="@(!fileSelected)" class="button scan-button">Scan QR Code</button>
    @if (!string.IsNullOrEmpty(scannedText))
    {
        <div class="result-section">
            <button @onclick="CopyToClipboard" class="button copy-button">Copy</button>
        </div>
    }
</div>
@page "/"
@using System.IO
@using Microsoft.AspNetCore.Components.Forms
@using IronQr
@using IronSoftware.Drawing
@inject IJSRuntime JSRuntime
<PageTitle>QR Code Scanner</PageTitle>
<div>
    <h1>QR Code Scanner</h1> 
    <InputFile OnChange="HandleSelectedFile" accept="image/*" class="file-input" />
    @if (!string.IsNullOrEmpty(qrImageSrc))
    {
        <img src="@qrImageSrcForDisplay" alt="QR Code Image" class="qr-image" />
    }
    <button @onclick="ScanQRCode" disabled="@(!fileSelected)" class="button scan-button">Scan QR Code</button>
    @if (!string.IsNullOrEmpty(scannedText))
    {
        <div class="result-section">
            <button @onclick="CopyToClipboard" class="button copy-button">Copy</button>
        </div>
    }
</div>
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: @page "/" using System.IO using Microsoft.AspNetCore.Components.Forms using IronQr using IronSoftware.Drawing inject IJSRuntime JSRuntime <PageTitle> QR Code Scanner</PageTitle> <div> <h1> QR Code Scanner</h1> <InputFile OnChange="HandleSelectedFile" accept="image/*" class="file-input" /> if(!string.IsNullOrEmpty(qrImageSrc))
"image/*" Class="file-input" /> [if](Not String.IsNullOrEmpty(qrImageSrc))
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: Friend @page "/" using System.IO using Microsoft.AspNetCore.Components.Forms using IronQr using IronSoftware.Drawing inject IJSRuntime JSRuntime <PageTitle> QR Code Scanner</PageTitle> <div> <h1> QR Code Scanner</h1> <InputFile OnChange="HandleSelectedFile" accept="image/*" Class
"HandleSelectedFile" accept="image/*" Class
Friend page "/" [using] System.IO [using] Microsoft.AspNetCore.Components.Forms [using] IronQr [using] IronSoftware.Drawing inject IJSRuntime JSRuntime (Of PageTitle) QR Code Scanner</PageTitle> (Of div) (Of h1) QR Code Scanner</h1> <InputFile OnChange="HandleSelectedFile" accept
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <img src="@qrImageSrcForDisplay" alt="QR Code Image" class="qr-image" />
		"QR Code Image" class="qr-image" />
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <img src="@qrImageSrcForDisplay" alt="QR Code Image" class
		"@qrImageSrcForDisplay" alt="QR Code Image" class
		<img src="@qrImageSrcForDisplay" alt
End Class
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <button onclick="ScanQRCode" disabled="@(!fileSelected)" class="button scan-button"> Scan QR Code</button> if(!string.IsNullOrEmpty(scannedText))
	"@(!fileSelected)" class="button scan-button"> Scan QR Code</button> [if](Not String.IsNullOrEmpty(scannedText))
	If True Then
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <button onclick="ScanQRCode" disabled="@(!fileSelected)" class
	"ScanQRCode" disabled="@(!fileSelected)" class
	<button onclick="ScanQRCode" disabled
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <div class="result-section"> <button onclick="CopyToClipboard" class="button copy-button"> Copy</button> </div>
		"CopyToClipboard" class="button copy-button"> Copy</button> </div>
'INSTANT VB WARNING: An assignment within expression was extracted from the following statement:
'ORIGINAL LINE: <div class="result-section"> <button onclick="CopyToClipboard" class
		"result-section"> <button onclick="CopyToClipboard" class
		<div class="result-section"> <button onclick
	End If
'INSTANT VB TODO TASK: The following line uses invalid syntax:
'</div>
VB   C#

Title and Heading: The <PageTitle> and <h1> tags define the title of the page and the main heading, respectively, setting the context for the user.

Image Upload Control: An <InputFile> component is used for uploading QR code images. This element is tailored to accept only image files, enhancing the user experience by filtering out non-relevant file types.

Image Display: Once an image is uploaded, it is displayed using an <img> tag. This visual feedback is crucial for user assurance that the correct file has been uploaded.

Scan Button: A button tagged with @onclick="ScanQRCode triggers the scanning process. Its availability depends on whether a file is selected, enhancing the interface's intuitiveness.

Result Display: The scanned QR code's text is displayed in a text input field for easy viewing. A separate button allows users to copy this text to the clipboard.

CSS Styling in site.css

The visual aesthetics and layout of the QR Code Scanner are defined in the site.css file.

.content {
    padding: 20px;
    margin: 10px auto; /* Centers the content */
    max-width: 500px; /* Sets a max width for the content */
    border-radius: 10px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.2);
    text-align: center;
}
.file-input, .result-input {
    margin: 10px 0;
    padding: 10px;
    border-radius: 5px;
    border: 1px solid #ddd;
    width: 100%;
}
.button {
    background-color: #4CAF50;
    color: white;
    border: none;
    cursor: pointer;
    padding: 10px;
    margin: 10px 0;
    border-radius: 5px;
    transition: background-color 0.3s, box-shadow 0.3s;
    width: auto; /* Adjusts button width */
    display: inline-block; /* Allows the width to adjust to content */
}
.button:hover {
    background-color: #45a049;
    box-shadow: 0 4px 8px rgba(0,0,0,0.2);
} 
.qr-image {
    max-width: 300px;
    max-height: 300px;
    display: block;
    margin: 10px auto;
    border-radius: 10px;
}
.result-section {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
}
.result-input {
    width: 100%;
    box-sizing: border-box;
}
.copy-button {
    margin-top: 10px;
    white-space: nowrap;
}

.content: This class styles the main content area, giving it a defined width, centered alignment, and subtle shadow for depth.

.file-input, .result-input: These classes style the file input and result display elements, ensuring they are visually consistent and fully occupy their container's width.

.button: Buttons are styled with a distinct green background, rounded corners, and a hover effect for better user interaction.

.qr-image: Styles applied to the QR code image include size constraints and auto margin for centering, making the image prominent yet not overwhelming.

.result-section: This class ensures that the elements within the result section are aligned centrally and spaced appropriately.

Handling File Uploads

The HandleSelectedFile method is a crucial part of the QR code scanning process, handling the user's file upload and preparing it for scanning. This method is triggered when the user selects a file through the <InputFile> component. This is shown in the following code:

private async Task HandleSelectedFile(InputFileChangeEventArgs e)
{
    selectedFile = e.File;
    fileSelected = true;
    var imagesDirectory = Path.Combine(Directory.GetCurrentDirectory(), "UploadedImages");
    Directory.CreateDirectory(imagesDirectory); // Ensure the directory exists
    // Use a GUID as the unique file name
    var uniqueFileName = Guid.NewGuid().ToString() + Path.GetExtension(selectedFile.Name);
    var fullPath = Path.Combine(imagesDirectory, uniqueFileName);
    await using (var fileStream = new FileStream(fullPath, FileMode.Create))
    {
        await selectedFile.OpenReadStream().CopyToAsync(fileStream);
    }
    // Store the full path in qrImageSrc for scanning
    qrImageSrc = fullPath;
    // Optionally, create a base64 string for displaying the image (if needed)
    byte[] imageBytes = await File.ReadAllBytesAsync(fullPath);
    var base64String = Convert.ToBase64String(imageBytes);
    qrImageSrcForDisplay = $"data:image/{Path.GetExtension(selectedFile.Name).TrimStart('.')};base64,{base64String}";
}
private async Task HandleSelectedFile(InputFileChangeEventArgs e)
{
    selectedFile = e.File;
    fileSelected = true;
    var imagesDirectory = Path.Combine(Directory.GetCurrentDirectory(), "UploadedImages");
    Directory.CreateDirectory(imagesDirectory); // Ensure the directory exists
    // Use a GUID as the unique file name
    var uniqueFileName = Guid.NewGuid().ToString() + Path.GetExtension(selectedFile.Name);
    var fullPath = Path.Combine(imagesDirectory, uniqueFileName);
    await using (var fileStream = new FileStream(fullPath, FileMode.Create))
    {
        await selectedFile.OpenReadStream().CopyToAsync(fileStream);
    }
    // Store the full path in qrImageSrc for scanning
    qrImageSrc = fullPath;
    // Optionally, create a base64 string for displaying the image (if needed)
    byte[] imageBytes = await File.ReadAllBytesAsync(fullPath);
    var base64String = Convert.ToBase64String(imageBytes);
    qrImageSrcForDisplay = $"data:image/{Path.GetExtension(selectedFile.Name).TrimStart('.')};base64,{base64String}";
}
Private Async Function HandleSelectedFile(ByVal e As InputFileChangeEventArgs) As Task
	selectedFile = e.File
	fileSelected = True
	Dim imagesDirectory = Path.Combine(Directory.GetCurrentDirectory(), "UploadedImages")
	Directory.CreateDirectory(imagesDirectory) ' Ensure the directory exists
	' Use a GUID as the unique file name
	Dim uniqueFileName = Guid.NewGuid().ToString() & Path.GetExtension(selectedFile.Name)
	Dim fullPath = Path.Combine(imagesDirectory, uniqueFileName)
'INSTANT VB TODO TASK: Local functions are not converted by Instant VB:
'	await using(var fileStream = New FileStream(fullPath, FileMode.Create))
'	{
'		await selectedFile.OpenReadStream().CopyToAsync(fileStream);
'	}
	' Store the full path in qrImageSrc for scanning
	qrImageSrc = fullPath
	' Optionally, create a base64 string for displaying the image (if needed)
	Dim imageBytes() As Byte = Await File.ReadAllBytesAsync(fullPath)
	Dim base64String = Convert.ToBase64String(imageBytes)
	qrImageSrcForDisplay = $"data:image/{Path.GetExtension(selectedFile.Name).TrimStart("."c)};base64,{base64String}"
End Function
VB   C#

Here's a detailed breakdown of its functionality:

File Selection and Validation: When a user uploads a file, the method captures the file's details using InputFileChangeEventArgs e. The selectedFile variable is then assigned to this file, and a boolean fileSelected is set to true, indicating that the input data/file is ready for processing.

Creating the File Path: The method prepares a directory to store the uploaded image. It uses Path.Combine to create a path to the 'UploadedImages' directory, ensuring it exists with the Directory.CreateDirectory. This step is crucial for organizing uploaded files systematically.

Generating a Unique Filename: To avoid conflicts with existing files, a unique filename is generated using a GUID (Globally Unique Identifier) appended with the original file's extension. This ensures each uploaded file is uniquePathdentified.

Saving the File: The file is then saved to the server. The method creates a file stream pointing to the newly generated file path, and the uploaded file's content is copied into this stream using await selectedFile.OpenReadStream().CopyToAsync(fileStream). This step finalizes the upload process.

Preparing the Image for Display: After the file is saved, it's necessary to display the image back to the user for confirmation. The method reads the file into a byte array and converts it into a base64 string, suitable for embedding directly into an <img> tag's src attribute. This conversion allows the image to be displayed without requiring a separate request to the server for the image file.

Scanning the QR Code

The ScanQRCode method is the heart of the QR code scanning functionality in the Blazor Server application. This method takes the uploaded image and uses IronQR to extract the QR code data.

private async Task ScanQRCode()
{   
    // Check if there is a valid image to work with
    if (string.IsNullOrEmpty(qrImageSrc)) return;
    try
    {
        var inputBmp = AnyBitmap.FromFile(qrImageSrc);
        QrImageInput imageInput = new QrImageInput(inputBmp);
        QrReader reader = new QrReader();
        IEnumerable<QrResult> results = reader.Read(imageInput);
        // Check if there are any results and if the first result contains text
        var firstResult = results.FirstOrDefault();
        if (firstResult != null && !string.IsNullOrWhiteSpace(firstResult.Value.ToString()))
        {
            scannedText = firstResult.Value.ToString();
        }
        else
        {
            scannedText = "QR value not found!";
        }
    }
    catch (Exception ex)
    {
        scannedText = "Error scanning QR code: " + ex.Message;
    }
}
private async Task ScanQRCode()
{   
    // Check if there is a valid image to work with
    if (string.IsNullOrEmpty(qrImageSrc)) return;
    try
    {
        var inputBmp = AnyBitmap.FromFile(qrImageSrc);
        QrImageInput imageInput = new QrImageInput(inputBmp);
        QrReader reader = new QrReader();
        IEnumerable<QrResult> results = reader.Read(imageInput);
        // Check if there are any results and if the first result contains text
        var firstResult = results.FirstOrDefault();
        if (firstResult != null && !string.IsNullOrWhiteSpace(firstResult.Value.ToString()))
        {
            scannedText = firstResult.Value.ToString();
        }
        else
        {
            scannedText = "QR value not found!";
        }
    }
    catch (Exception ex)
    {
        scannedText = "Error scanning QR code: " + ex.Message;
    }
}
Private Async Function ScanQRCode() As Task
	' Check if there is a valid image to work with
	If String.IsNullOrEmpty(qrImageSrc) Then
		Return
	End If
	Try
		Dim inputBmp = AnyBitmap.FromFile(qrImageSrc)
		Dim imageInput As New QrImageInput(inputBmp)
		Dim reader As New QrReader()
		Dim results As IEnumerable(Of QrResult) = reader.Read(imageInput)
		' Check if there are any results and if the first result contains text
		Dim firstResult = results.FirstOrDefault()
		If firstResult IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(firstResult.Value.ToString()) Then
			scannedText = firstResult.Value.ToString()
		Else
			scannedText = "QR value not found!"
		End If
	Catch ex As Exception
		scannedText = "Error scanning QR code: " & ex.Message
	End Try
End Function
VB   C#

Initially, the method checks if the qrImageSrc variable, which holds the path to the uploaded image, is not empty. This check ensures that there is a valid image to work with before proceeding.

Once it's confirmed that an image is ready for processing, the method proceeds to the core functionality of QR code reading. This involves a few key steps, starting with loading the image from its stored location into a format suitable for QR code analysis. This conversion is made possible by the AnyBitmap.FromFile(qrImageSrc) method, which prepares the image for the scanning process.

The next step involves the creation of a QrReader object. This object is integral to the IronQR library, serving as the primary tool for decoding QR codes from images. With the QrReader instance ready, the application then proceeds to scan the uploaded image. The reader.Read(imageInput) function is responsible for this action, methodically searching the image for QR codes and extracting their data.

The results of the scan are stored in a IEnumerable<QrResult> collection. This collection is then scrutinized to find the first QR code result. If a QR code is detected, and it contains readable text, this text is captured and stored in the scannedText variable. However, in scenarios where a QR code is either not found or does not contain text, the application sets a default message to inform the user that no QR value was detected.

Once the QR code is successfully scanned, the text string is displayed in a text input field, thanks to the two-way data binding capabilities of Blazor. This is achieved by binding the scannedText variable to a text input element. The input field is set to be disabled, making it read-only. This design choice focuses the user's interaction towards viewing the result and copying it rather than editing the content.

The entire scanning process is enclosed within a try-catch block, safeguarding against any unforeseen errors during the scanning operation. This could include issues related to the image file format or unexpected errors during the reading process. If an exception occurs, it's caught, and an error message is formulated and displayed to the user. This approach not only helps in identifying issues but also maintains transparency with the user, enhancing the application's reliability.

Copying the Result

To enable the copy-to-clipboard feature, a JavaScript function named copyTextToClipboard is defined in the _Host.cshtml file. This script is a simple yet effective way to interact with the clipboard:

<script>
    function copyTextToClipboard(text) {
        navigator.clipboard.writeText(text).then(function () {
            console.log('Copying to clipboard was successful!');
        }, function (err) {
            console.error('Could not copy text: ', err);
        });
    }
</script>
JAVASCRIPT

This function accepts a text parameter, which is the text to be copied. It uses the navigator.clipboard.writeText method, a modern approach to interact with the clipboard. This method is preferred for its simplicity and compliance with web standards. It is designed to log a success message in the console upon successful copying, aiding in debugging and ensuring smooth functionality. In case of an error, an error message is logged to the console, providing insights into any issues encountered during the operation.

The CopyToClipboard method is in the @code part of the index.razor serves as a bridge between the Blazor app and the JavaScript function. A button triggers this method click in the user interface. When activated, it invokes the copyTextToClipboard JavaScript function using Blazor's JavaScript InterOp capabilities. The scannedText is passed as an argument to this function, effectively copying the text to the user's clipboard.

private async Task CopyToClipboard()
{
    await JSRuntime.InvokeVoidAsync("copyTextToClipboard", scannedText);
}
private async Task CopyToClipboard()
{
    await JSRuntime.InvokeVoidAsync("copyTextToClipboard", scannedText);
}
Private Async Function CopyToClipboard() As Task
	Await JSRuntime.InvokeVoidAsync("copyTextToClipboard", scannedText)
End Function
VB   C#

Executing The Application

Upon running the project, the user will see the following clean and simple interface. The initial screen showcases the QR Code Scanner module prominently. This module includes a button to upload the QR code image file ('Choose File') and another to initiate the scanning process ('Scan QR Code'). Initially, no file is selected, and the scanning area is blank, awaiting user input.

How To Create a Blazor QR Code Scanner: Figure 6 - Result of initial execution of the project

The user selects and uploads a QR code image using the 'Choose File' button, which now displays the name of the selected file (e.g., 'qrvalue.png'). The uploaded QR code is visible in the designated area on the interface, confirming to the user that the image is ready to be scanned.

How To Create a Blazor QR Code Scanner: Figure 7 - The result of the user imputing a QR code

After the user clicks the 'Scan QR Code' button, the application processes the image. If the scan is successful, the text encoded within the QR code is displayed just below the image. In this case, the scanned result ( '<https://ironsoftware.com/csharp/qr/>') is a URL indicating where the QR code would direct a user when scanned with a QR reader. Copy button appears alongside the result, allowing the user to easily copy the scanned text to the clipboard for further use.

How To Create a Blazor QR Code Scanner: Figure 8 - This shows the text from the QR code and the copy button

Conclusion

How To Create a Blazor QR Code Scanner: Figure 9

In summary, the process of integrating the IronQR into a Blazor Server application is smooth and effective, resulting in a QR code scanning solution. Responsive and easy to use, from the start of setting up this project through to its implementation of scanning functionality, thanks to a mixture of IronQR's powerful processing with Blazor's dynamic UI rendering. The process, from setting up the environment to deployment, emphasizes the practicality and effectiveness of this integration in real-world applications. While IronQR is adept at QR codes, for projects requiring the scan barcode feature, IronBarcode is an ideal option, offering a similar level of ease and integration.

IronQR offers a free trial for developers to explore its features before purchasing. For extended use and access to all its professional features in production, IronQR licenses start at $599.