Silent Excel Spreadsheet XLS Printing with Pages Range Settings from Blazor
Product JSPrintManager for Blazor Published 07/09/2021 Updated 07/09/2021 Author Neodynamic
Overview
In this walkthrough, you'll learn how to silently print XLS/x files from any Razor page directly to the client printer without displaying any print dialog and by writing pure Blazor code. You'll be able to print XLS/x files to the Default client printer as well as to any other installed printer at the client machine with advanced settings like Pages Range!
Client-side Requirements:
- Available for Windows clients only
- Microsoft Excel 97+ must be installed at the client machine
- Spreadsheet files can be any of these file formats: *.xl, *.xlsx, *.xlsm, *.xlsb, *.xlam, *.xltx, *.xltm, *.xls, *.xla, *.xlt, *.xlm, *.xlw and *.ods
Follow up these steps
- Be sure you install in your dev machine JSPrintManager (JSPM) (Available for Windows, Linux, Raspberry Pi & Mac)
This small app must be installed on each client that will print from your website! - In your Blazor project...
- Add a NuGet reference to the JSPrintManager Razor Component
- Add the JSPrintManager service...
- Add the following statement at the top of your
Startup
file
using Neodynamic.Blazor;
- For Blazor Server
Add the following line in theStartup's ConfigureServices
method
services.AddJSPrintManager();
- For Blazor WebAssembly
Add the following line in theProgram's Main
method
builder.Services.AddJSPrintManager();
- For Blazor Server
- Add the following statement at the top of your
- Add the following statement in the
_Imports.razor
file
@using Neodynamic.Blazor
- Add a new Razor Page and copy/paste the following code. Please read the source code comments to understand the printing logic!
@page "/" @inject JSPrintManager JSPrintManager @using System @using System.IO <div> <strong>JSPM </strong><span>WebSocket Status </span> @if (JSPrintManager.Status == JSPMWSStatus.Open) { <span class="badge badge-success"> <i class="fa fa-check" /> Open </span> } else if (JSPrintManager.Status == JSPMWSStatus.Closed) { <span class="badge badge-danger"> <i class="fa fa-exclamation-circle" /> Closed! </span> <div> <strong>JSPrintManager (JSPM) App</strong> is not installed or not running! <a href="https://neodynamic.com/downloads/jspm" target="_blank">Download JSPM Client App...</a> </div> } else if (JSPrintManager.Status == JSPMWSStatus.Blocked) { <span class="badge badge-warning"> <i class="fa fa-times-circle" /> This Website is Blocked! </span> } else if (JSPrintManager.Status == JSPMWSStatus.WaitingForUserResponse) { <span class="badge badge-warning"> <i class="fa fa-user-circle" /> Waiting for user response... </span> } </div> @if (JSPrintManager.Status == JSPMWSStatus.Open) { @if (JSPrintManager.Printers == null) { <hr /> <div class="spinner-border text-info" role="status"> <span class="sr-only">Please wait...</span> </div> <strong><em>Getting local printers...</em></strong> } else { <div class="row"> <div class="col-md-12"> <h2 class="text-center"> <i class="fa fa-files-o" /> Advanced XLS Printing from Blazor </h2> <hr /> </div> </div> <EditForm Model="@MyCPJ"> <div class="row"> <div class="col-md-6"> <div class="bg-light"> <strong>Print Local XLS File</strong> <br /> <InputFile OnChange="@LoadFiles" class="form-control-file" accept=".xls,.xlsx" /> <br /> @if (isLoading) { <p><em><small>Loading files...</small></em></p> <div class="progress"> <div class="progress-bar bg-info" role="progressbar" style="width: @(loadingStep)%;" aria-valuenow="@(loadingStep)" aria-valuemin="0" aria-valuemax="100">@(loadingStep)%</div> </div> } </div> </div> <div class="col-md-6"> <div class="bg-light"> <strong>Print XLS File from URL</strong> <br /> <div> <small> <strong>Predefined Sample:</strong> </small> <button type="button" class="btn btn-warning btn-sm" @onclick="@(() => SetFilePath("https://neodynamic.com/temp/Project-Scheduling-Monitoring-Tool.xls"))"> <i class="fa fa-arrow-circle-down" /> Project-Scheduling-Monitoring-Tool.xls </button> </div> <InputText Value="@MyFilePath" ValueExpression="@(() => MyFilePath)" ValueChanged="@SetFilePath" class="form-control form-control-sm text-monospace" /> </div> </div> </div> </EditForm> <div class="row"> <div class="col-md-12"> <EditForm Model="@MyPrinter"> <p>Select an <strong>Installed Printer</strong>:</p> <InputSelect @bind-Value="MyPrinter.PrinterName" class="form-control form-control-sm"> @foreach (var p in JSPrintManager.Printers) { <option value="@p">@p</option> } </InputSelect> </EditForm> </div> </div> <div class="row"> <div class="col-md-3"> <label>Page From:</label> <EditForm Model="@MyXlsFile"> <InputNumber @bind-Value="MyXlsFile.PageFrom" class="form-control form-control-sm" /> </EditForm> </div> <div class="col-md-3"> <label>Page To:</label> <EditForm Model="@MyXlsFile"> <InputNumber @bind-Value="MyXlsFile.PageTo" class="form-control form-control-sm" /> </EditForm> </div> </div> <div class="row"> <div class="col-md-12"> <br /> <div class="text-center"> <button class="btn btn-success btn-lg" @onclick="DoPrinting"> <i class="fa fa-print" /> Print Now... </button> </div> </div> </div> } } @code { protected override void OnAfterRender(bool firstRender) { if (firstRender) { // Handle OnGetPrinters event... JSPrintManager.OnGetPrinters += () => { if (JSPrintManager.Printers != null && JSPrintManager.Printers.Length > 0) { // Display installed printers... StateHasChanged(); } else { Console.WriteLine("No printers found..."); } }; // Handle OnStatusChanged event to detect any WSS status change JSPrintManager.OnStatusChanged += () => { StateHasChanged(); // Status = Open means that JSPM Client App is up and running! if (JSPrintManager.Status == JSPMWSStatus.Open) { //Try getting local printers... JSPrintManager.TryGetPrinters(); } }; // Start WebSocket comm JSPrintManager.Start(); } base.OnAfterRender(firstRender); } private ClientPrintJob MyCPJ { get; set; } = new(); private InstalledPrinter MyPrinter { get; set; } = new(); private PrintFileXLS MyXlsFile { get; set; } = new(); private void DoPrinting() { // set target printer MyCPJ.ClientPrinter = MyPrinter; // set xls file source... if (FileFromUrl) { MyXlsFile.FileContentType = FileSourceType.URL; MyXlsFile.FileContent = MyFilePath; MyXlsFile.FileName = MyFilePath.Substring(MyFilePath.LastIndexOf('/') + 1); } else { foreach (var fileEntry in loadedFiles) { MyXlsFile.FileContentType = FileSourceType.Base64; MyXlsFile.FileContent = fileEntry.Value; MyXlsFile.FileName = fileEntry.Key; } } // Add the xls file to the print job MyCPJ.Files.Clear(); MyCPJ.Files.Add(MyXlsFile); // Send job to the client! JSPrintManager.SendClientPrintJob(MyCPJ); } #region File Source Handling private bool FileFromUrl = false; private string MyFilePath { get; set; } private void SetFilePath(string filePath) { MyFilePath = filePath; FileFromUrl = true; } private Dictionary<string, byte[]> loadedFiles = new(); private int maxAllowedFiles = 1; private int maxSizeFile = 5000000; //5MB private bool isLoading = false; private int loadingStep = 0; private async Task LoadFiles(InputFileChangeEventArgs e) { isLoading = true; loadedFiles.Clear(); loadingStep = 0; var numOfFiles = Math.Min(e.FileCount, maxAllowedFiles); var i = 1; foreach (var file in e.GetMultipleFiles(maxAllowedFiles)) { try { await using MemoryStream ms = new(); await file.OpenReadStream(maxSizeFile).CopyToAsync(ms); loadedFiles.Add(file.Name, ms.ToArray()); loadingStep = (int)(((float)i / (float)numOfFiles) * 100f); StateHasChanged(); } catch (Exception ex) { } i++; } FileFromUrl = false; isLoading = false; } #endregion }
- That's it! Run your website and test it. Click on Print Now... to print the Excel file without print dialog. You can print it to the Default client printer or you can get a list of the installed printers available at the client machine.