As the title of this blog post suggests, we now offer a way for you to use DevExpress Reports within your server-side 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:
- Visual Studio 2019 with the ASP.NET and Web Development workload
- .NET Core 3 Framework
- DevExpress Subscription that includes Reporting tools
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.
Configure the Server Project
Install the
DevExpress.AspNetCore.Reporting
NuGet package.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
andDXQB
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);
}
}
}
Install the
Microsoft.AspNetCore.Mvc.NewtonsoftJson
NuGet package to help handle sending the model to the client in the JSON format.Add server-side storage to save and load the reports. Create a new class (
CustomReportStorageWebExtension
), inherit it from theReportStorageWebExtension
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.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
- 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",
},
// ...
}
Run the
npm install
command to install these packages.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");
}
}
- 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:
- 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);
}
}
- 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";
// ...
- (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:
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.