Reporting for Blazor - How to use Document Viewer and Report Designer within your Blazor Apps

ASP.NET Team Blog
03 December 2019



As the title of this blog post suggests, we now offer a way for you to use DevExpress Reports within your Blazor apps.

In this post I'll show you how to integrate the HTML5 Document Viewer and End-User Report Designer, part of our excellent reporting tools, into your Blazor applications. I'll focus on integration details and create a sample reporting application as well. Let's get started.

Prerequisites

Here’s what you’ll need to use DevExpress Reporting (our HTML5 Document Viewer and End-User Report Designer) with the Blazor framework:

Source Code

You can find the source code of the sample below on GitHub.

How to Add Reporting to Blazor

To get started, you must first create a new Blazor application using the Blazor WebAssembly App template and check the ASP.NET Core hosted option (or run dotnet new blazorwasm –hosted command). If this template was not installed, please review the following document: Get started with ASP.NET Core Blazor.

This solution uses the ASP.NET Core backend (server-side__Blazor) to process requests from the Document Viewer and Report Designer. The client side defines the UI for these components and the logic to respond to UI updates.

DevExpress Reports for Blazor

Configure the Server Project

  1. Install the DevExpress.AspNetCore.Reporting NuGet package.

  2. Create a custom controller and add an action method to generate the Report Designer model based on the report URL and URIs of reporting controllers. This example uses the default DXXRD , DXXRDV and DXQB controllers of our Reporting tools to process requests from the Report Designer, Document Viewer, and Query Builder, respectively:

namespace BlazorReporting.Server.Controllers
{
    [Route("api/[controller]")]
    public class ReportingController : Controller
    {
        public ReportingController() { }
        [Route("[action]", Name = "getReportDesignerModel")]
        public object GetReportDesignerModel(string reportUrl)
        {
            string modelJsonScript = new ReportDesignerClientSideModelGenerator(HttpContext.RequestServices)
                .GetJsonModelScript(reportUrl, null, "/DXXRD", "/DXXRDV", "/DXQB");
            return new JavaScriptSerializer().Deserialize<object>(modelJsonScript);
        }
    }
}
  1. Install the Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet package to help handle sending the model to the client in the JSON format.

  2. Add server-side storage to save and load the reports. Create a new class (CustomReportStorageWebExtension), inherit it from the ReportStorageWebExtension class and define the methods. In this example, our reports are stored in memory, but you can add other storage types (database, file system, etc.) See Implement a Report Storage for more information.

  3. Open the Startup file and register the reporting services and storage:

using DevExpress.AspNetCore;
using DevExpress.AspNetCore.Reporting;
// ...
public void ConfigureServices(IServiceCollection services)
{
    services.AddDevExpressControls();
    services.AddMvc().AddNewtonsoftJson().AddDefaultReportingControllers();
    // ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension.RegisterExtensionGlobal(new CustomReportStorageWebExtension());
    app.UseStaticFiles();
    app.UseDevExpressControls();
    // ...
}

Configure the Client Project

  1. Add the package.json configuration file and list the following npm packages required by our Reporting components:
{
  "dependencies": {
    // ...
    "devexpress-reporting": "~19.2.3"
    "@devexpress/analytics-core": "~19.2.3",
    "devextreme": "~19.2.3",
  },
  // ...
}
  1. Run the npm install command to install these packages.

  2. Create the Viewer.razor file and use the dxReportViewer binding to render the Document Viewer:

@page "/viewer"
@inject IJSRuntime JSRuntime
@implements IDisposable

<div id="viewer" style="width:1000px; height: 800px" data-bind="dxReportViewer: $data"></div>

@code {
    protected override void OnAfterRender(bool firstRender)
    {
        JSRuntime.InvokeAsync<object>("JsFunctions.InitWebDocumentViewer");
    }

    public void Dispose()
    {
        JSRuntime.InvokeAsync<string>("JsFunctions.Dispose", "viewer");
    }
}
  1. Create the Designer.razor file and use the dxReportDesigner binding to render the Report Designer:
@page "/designer"
@inject IJSRuntime JSRuntime
@implements IDisposable

<div style="width:1000px; height:800px" id="designer" data-bind="dxReportDesigner: $data"></div>

@code {
    protected override void OnAfterRender(bool firstRender)
    {
        JSRuntime.InvokeAsync<object>("JsFunctions.InitReportDesigner");
    }

    public void Dispose()
    {
        JSRuntime.InvokeAsync<string>("JsFunctions.Dispose", "designer");
    }
}

You should use the OnAfterRender lifecycle method for both components for initialization and the Dispose method to release resources used by these components:

  1. Add the index.js file and implement the logic to initialize and dispose the components:
var ko = require('knockout');

require('devexpress-reporting/dx-reportdesigner');

window.JsFunctions = {
    _viewerOptions: {
        reportUrl: ko.observable("MyReport"),
        requestOptions: {
            invokeAction: "/DXXRDV"
        }
    },
    _designerOptions: {
        reportUrl: ko.observable("MyReport"),
        requestOptions: {
            getDesignerModelAction: "api/Reporting/getReportDesignerModel"
        }
    },
    InitWebDocumentViewer: function () {
        ko.applyBindings(this._viewerOptions, document.getElementById("viewer"));
    },
    InitReportDesigner: function () {
        ko.applyBindings(this._designerOptions, document.getElementById("designer"));
    },
    Dispose: function (elementId) {
        var element = document.getElementById(elementId);
        element && ko.cleanNode(element);
    }
}
  1. Create a new style.css file and import CSS styles:
@import url("node_modules/jquery-ui/themes/base/all.css");
@import url("node_modules/devextreme/dist/css/dx.common.css");
@import url("node_modules/devextreme/dist/css/dx.light.css");
@import url("node_modules/@devexpress/analytics-core/dist/css/dx-analytics.common.css");
@import url("node_modules/@devexpress/analytics-core/dist/css/dx-analytics.light.css");
@import url("node_modules/@devexpress/analytics-core/dist/css/dx-querybuilder.css");
@import url("node_modules/devexpress-reporting/dist/css/dx-webdocumentviewer.css");
@import url("node_modules/devexpress-reporting/dist/css/dx-reportdesigner.css");

Add a reference to this new style.css file in the index.js file:

require('devexpress-reporting/dx-reportdesigner');
import "./style.css";
// ...
  1. (Optional) You can create a bundle with Webpack as demonstrated in the GitHub example. Add a new webpack.config.js file, define the index.js file as the bundle's entry and the bundle.js file as the output file.
module.exports = {
    entry: './src/index.js',
    mode: "development",
    output: {
        path: path.resolve(__dirname, '../wwwroot/site'),
        filename: "bundle.js"
    },
    // ...
};

Run the webpack command within the console to create the bundle. Link resulting files in the index.html file.

Run and View Report

Run the solution and view our sample in your local browser:

Blazor Reporting - Web Report Designer

Please test this example and share your thoughts in the survey at the end of this post.

Your Feedback Counts

As always, we welcome your feedback. Please take a moment to answer the following survey question so we can better understand your Blazor reporting requirements.

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.