Skip to main content
A newer version of this page is available. .

Create a Blazor Reporting (JavaScript-Based) Application

  • 8 minutes to read

This tutorial describes how to create a sample Blazor application that contains the Document Viewer and Report Designer controls.

View Example: Reporting Components for Blazor - Getting Started

Prerequisites

  1. Install the .NET 5.0 SDK.
  2. Install Visual Studio 2019 (latest version) with the ASP.NET and web development workload.
  3. Use the DevExpress .NET Product Installer to install DevExpress Blazor components.

When you use the DevExpress .NET Product Installer to install Blazor components, the DevExpress Template Gallery automatically appears in Visual Studio’s New Project menu. The Gallery contains DevExpress Blazor project templates.

Create a New Blazor Application

Perform the following steps to create a sample Blazor application in Visual Studio:

  1. Click Create a new project on Visual Studio’s start page and select the DevExpress v21.2 Blazor App Template Gallery.

    Create a new project

    Click Next.

  2. Specify the project name and location.

    Blazor Configure New Project

    Click Create.

  3. Select the Blazor Server Application.

    Blazor Select Server Application

    Click Run Wizard.

  4. The DevExpress Blazor Project Wizard allows you to select a theme to change the appearance of all components, and specify the culture used to localize UI elements. Click Create Project.

    DevExpress Blazor Wizard

The Server Application template creates a project that includes the following:

Install the DevExpress Blazor Reporting NuGet Package

  1. Right-click the Dependencies node in the Solution Explorer and select Manage NuGet Packages….

  2. In the invoked dialog, click the Browse tab, select the DevExpress 21.2 Local package source, and install the DevExpress.Blazor.Reporting.JSBasedControls NuGet package.

    The DevExpress 21.2 Local package is automatically added as a package source to your NuGet configuration files if you use the DevExpress .NET Product Installer.

    Blazor Install NuGet Package Native Viewer

    Note

    The DevExpress.Blazor.Reporting.Viewer NuGet package contains the Report Viewer native component for Blazor.

    The DevExpress.Blazor.Reporting.JSBasedControls NuGet package contains the Document Viewer and Report Designer JavaScript-based components.

    The DevExpress.Blazor.Reporting NuGet package contains all Blazor Reporting components - the Report Viewer, Document Viewer, and Report Designer. This is a legacy package for Blazor Reporting apps. Please use one of the packages mentioned above for new projects.

  3. Build the project.

Add Dependencies

  1. Modify the Startup.cs file. Call the AddDevExpressBlazorReporting method that sets up the DevExpress control services and the UseDevExpressBlazorReporting method that registers DevExpress middleware components:

    using DevExpress.Blazor.Reporting;
    // ...
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDevExpressBlazorReporting();
        // ...
    }
    
    using DevExpress.Blazor.Reporting;
    // ...
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseDevExpressBlazorReporting();
        // ...
    }        
    
  2. Modify the _Imports.razor file to register the DevExpress.Blazor.Reporting namespace:

    @using DevExpress.Blazor.Reporting        
    

Add the Report Storage

  1. Implement the ReportStorageWebExtension descendant that saves and loads reports. The code below is a sample storage that uses the file system to store reports:

    using System;
    using Microsoft.AspNetCore.Hosting;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using DevExpress.XtraReports.UI;
    using DevExpress.XtraReports.Web.Extensions;
    
    namespace BlazorApp1
    {
        public class CustomReportStorageWebExtension : ReportStorageWebExtension
        {
            readonly string ReportDirectory;
            const string FileExtension = ".repx";
            public CustomReportStorageWebExtension(IWebHostEnvironment env)
            {
                ReportDirectory = Path.Combine(env.ContentRootPath, "Reports");
                if (!Directory.Exists(ReportDirectory))
                {
                    Directory.CreateDirectory(ReportDirectory);
                }
            }
    
            private bool IsWithinReportsFolder(string url, string folder)
            {
                var rootDirectory = new DirectoryInfo(folder);
                var fileInfo = new FileInfo(Path.Combine(folder, url));
                return fileInfo.Directory.FullName.ToLower().StartsWith(rootDirectory.FullName.ToLower());
            }
    
            public override bool CanSetData(string url)
            {
                // Determines whether it is possible to store a report by a given URL. 
                // For instance, make the CanSetData method return false for reports
                // that should be read-only in your storage. 
                // This method is called only for valid URLs
                // (if the IsValidUrl method returns true) before calling the SetData method.
    
                return true;
            }
    
            public override bool IsValidUrl(string url)
            {
                // Determines whether the URL passed to the current Report Storage is valid. 
                // For instance, implement your own logic to prohibit URLs that contain white spaces
                // or other special characters. 
                // This method is called before the CanSetData and GetData methods.
    
                return Path.GetFileName(url) == url;
            }
    
            public override byte[] GetData(string url)
            {
                // Returns report layout data stored in a Report Storage using the specified URL. 
                // This method is called only for valid URLs after the IsValidUrl method is called.
                try
                {
                    if (Directory.EnumerateFiles(ReportDirectory)
                        .Select(Path.GetFileNameWithoutExtension).Contains(url))
                    {
                        return File.ReadAllBytes(Path.Combine(ReportDirectory, url + FileExtension));
                    }
                }
                catch (Exception ex)
                {
                    throw new DevExpress.XtraReports.Web.ClientControls.FaultException(
                        "Could not get report data.", ex);
                }
                throw new DevExpress.XtraReports.Web.ClientControls.FaultException(
                    string.Format("Could not find report '{0}'.", url));
            }
    
            public override Dictionary<string, string> GetUrls()
            {
                // Returns a dictionary of the existing report URLs and display names. 
                // This method is called when running the Report Designer, 
                // before the Open Report and Save Report dialogs are shown
                // and after a new report is saved to storage.
    
                return Directory.GetFiles(ReportDirectory, "*" + FileExtension)
                                        .Select(Path.GetFileNameWithoutExtension)
                                        .ToDictionary<string, string>(x => x);
            }
    
            public override void SetData(XtraReport report, string url)
            {
                // Stores the specified report to a Report Storage using the specified URL. 
                // This method is called only after the IsValidUrl and CanSetData methods are called.
                if (!IsWithinReportsFolder(url, ReportDirectory))
                    throw new DevExpress.XtraReports.Web.ClientControls.FaultException(
                        "Invalid report name.");
                report.SaveLayoutToXml(Path.Combine(ReportDirectory, url + FileExtension));
            }
    
            public override string SetNewData(XtraReport report, string defaultUrl)
            {
                // Stores the specified report using a new URL. 
                // The IsValidUrl and CanSetData methods are never called before this method. 
                // You can validate and correct the specified URL directly
                // in the SetNewData method implementation 
                // and return the resulting URL used to save a report in your storage.
                SetData(report, defaultUrl);
                return defaultUrl;
            }
        }
    }
    
  1. Modify the Startup.cs file and add the code that registers the CustomReportStorageWebExtension:

    using DevExpress.XtraReports.Web.Extensions;
    // ...
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDevExpressBlazorReporting();
        // Register the storage after the AddDevExpressBlazorReporting method call.
        services.AddScoped<ReportStorageWebExtension, CustomReportStorageWebExtension>();
        // ...
    
    }
    

    Important

    Register the ReportStorageWebExtension after the AddDevExpressBlazorReporting method.

  2. Add a new Reports folder to a project.

    Tip

    The name Reports is specified in the ReportStorageWebExtension implementation.

Create a Sample Report

Note

To perform this step, you should install DevExpress Reporting on your machine. Refer to the following topic for more information: Install All DevExpress .NET Framework and .NET 5+ Products.

  1. Select Project -> Add New Item… to invoke the Add New Item dialog. Navigate to the Reporting node and select the DevExpress v.21.2 Report item template.

    Add a New Report

    Name the report TestReport.cs and click Add.

  2. Select Blank In the invoked Report Wizard page and click Finish. The newly created report is loaded in the Visual Studio Report Designer.

  3. Modify a newly created report in Visual Studio Report Designer. Add a label and type in the Hello, World! text:

    Edit a Report in a VS Designer

  4. Click the report’s smart tag (a button in the upper left corner of the Designer window) and select Save…:

    save-report-defintion-visual-studio-design-time

    In the invoked Save As dialog, specify the Reports project folder, Report XML Files (.repx) file type and the TestReport.repx file name.

Create Routing to Controller Actions

Modify the Startup.cs file. Add the following code to map requests to controller actions:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // ...
        app.UseEndpoints(endpoints =>
            {
            endpoints.MapControllers();
            // ...                
            });
    }

Implement Default Controllers

  1. In Visual Studio, create a new Controllers folder.
  2. Add a new class file (ReportingControllers.cs) with the following content:

    using DevExpress.AspNetCore.Reporting.QueryBuilder;
    using DevExpress.AspNetCore.Reporting.QueryBuilder.Native.Services;
    using DevExpress.AspNetCore.Reporting.ReportDesigner;
    using DevExpress.AspNetCore.Reporting.ReportDesigner.Native.Services;
    using DevExpress.AspNetCore.Reporting.WebDocumentViewer;
    using DevExpress.AspNetCore.Reporting.WebDocumentViewer.Native.Services;
    // ...
        public class CustomWebDocumentViewerController : WebDocumentViewerController {
            public CustomWebDocumentViewerController(IWebDocumentViewerMvcControllerService controllerService) : base(controllerService) {
            }
        }
    
        public class CustomReportDesignerController : ReportDesignerController {
            public CustomReportDesignerController(IReportDesignerMvcControllerService controllerService) : base(controllerService) {
            }
        }
    
        public class CustomQueryBuilderController : QueryBuilderController {
            public CustomQueryBuilderController(IQueryBuilderMvcControllerService controllerService) : base(controllerService) {
            }
        }
    

Add Document Viewer to a Page

Create a new Razor component (select Project -> Add New Item.. -> Razor Component). Save it as the DocumentVIewer.razor file, and open and overwrite its content with the following:

@page "/documentviewer"

<DxDocumentViewer ReportName="TestReport" Height="1000px" Width="100%">
        <DxDocumentViewerTabPanelSettings Width="340" />
</DxDocumentViewer>

The DxDocumentViewer component invokes the default WebDocumentViewerController that processes requests from the Document Viewer and loads the TestReport report to create a document.

Add Report Designer to a Page

Create a new Razor component (select Project -> Add New Item.. -> Razor Component). Save it as the ReportDesigner.razor file, and open and overwrite its content with the following:

@page "/reportdesigner"

    <DxReportDesigner ReportName="TestReport" Height="1000px" Width="100%" AllowMDI="true">     
        <DxReportDesignerWizardSettings UseFullscreenWizard="true" />
    </DxReportDesigner>

The DxReportDesigner loads the TestReport report.

Note

If you implement a custom report that inherits from XtraReport and want to open it in the End-User Report Designer, add a constructor without parameters to this report.

Modify the NavMenu.razor file to include links to the newly created pages:

<div class="sidebar @StateCssClass">
<DxTreeView AllowSelectNodes="true" CssClass="app-sidebar">
    <Nodes>
        <DxTreeViewNode NavigateUrl="./" Text="Overview"></DxTreeViewNode>
        <DxTreeViewNode NavigateUrl="datagrid" Text="Data Grid"></DxTreeViewNode>
        <DxTreeViewNode NavigateUrl="documentviewer" Text="Document Viewer"></DxTreeViewNode>
        <DxTreeViewNode NavigateUrl="reportdesigner" Text="Report Designer"></DxTreeViewNode>
    </Nodes>
</DxTreeView>
</div>

@code {
    [Parameter] public string StateCssClass { get; set; }
}

Run the Project

Run the project. The Document Viewer and Report Designer components load the TestReport.repx report: