Skip to main content
All docs
V25.2
  • Add a JavaScript-Based Report Designer to a Blazor Web App (Interactive WebAssembly) with an ASP.NET Core Backend (Visual Studio)

    • 7 minutes to read

    This tutorial adds a Report Designer (DxWasmReportDesigner) to an application created with the Blazor Web App (Interactive WebAssembly) template.

    Create a New Project from a Template

    This section describes how to create a new Blazor project. If you wish to start this tutorial using an existing application, go to Step 2.

    1. Click Create a new project on Visual Studio’s start page and select the Blazor Web App template. Select WebAssembly from the Interactive Render Mode drop-down list.

      Blazor Web App Template — Additional Settings

      Click Next.

    2. Specify the project name and location, and click Next.

    3. Specify additional options and click Create.

    You can use this tutorial to integrate the Standalone Report Parameters Panel into a Blazor WebAssembly Hosted (available in ASP.NET Core 7.0 or earlier) template.

    For additional information on available Blazor templates, refer to the following help topic: Tooling for ASP.NET Core Blazor.

    Install NuGet Packages

    1. Install the following NuGet packages to the projects:

      Project Package
      BlazorWasmReportingApp DevExpress.AspNetCore.Reporting
      BlazorWasmReportingApp.Client DevExpress.Blazor.Reporting.JSBasedControls.WebAssembly

      For additional information on how to install NuGet packages for DevExpress components, review the following help topic: Choose Between Offline and Online DevExpress NuGet Feeds.

    2. Build the project.

    Register DevExpress Resources

    1. In the Program.cs file of the main project, add the following code:

      using BlazorWasmReportingApp.Server;
      using DevExpress.AspNetCore;
      using DevExpress.AspNetCore.Reporting;
      using DevExpress.XtraReports.Web.Extensions;
      using Microsoft.AspNetCore.ResponseCompression;
      
      var builder = WebApplication.CreateBuilder(args);
      
      // Add services to the container.
      builder.Services.AddControllersWithViews();
      builder.Services.AddRazorPages();
      builder.Services.AddDevExpressControls();
      
      var app = builder.Build();
      
      // ...
      app.UseDevExpressControls();
      // ...
      app.Run();
      
    2. In the _Imports.razor file of the main project, register the DevExpress.Blazor namespace:

      @using DevExpress.Blazor
      
    3. In the App.razor file of the main project, call the RegisterScripts(Action<ResourcesConfigurator>) method to register DevExpress client resources:

      <head>
      @*...*@
      @DxResourceManager.RegisterScripts()
      @*...*@
      </head>
      

    Implement Reporting Controllers

    1. Create a Controllers folder in the main project.
    2. In the created folder, add a new ReportingControllers.cs class file 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(IReportDesignerMvcControllerService controllerService)
          : ReportDesignerController(controllerService) {
          [HttpPost("[action]")]
          public async Task<object> GetReportDesignerModel(
              [FromForm] string reportUrl,
              [FromForm] ReportDesignerSettingsBase designerModelSettings,
              [FromServices] IReportDesignerClientSideModelGenerator designerClientSideModelGenerator) {
              Dictionary<string, object> dataSources = new();
              ReportDesignerModel model = string.IsNullOrEmpty(reportUrl) ?
                  await designerClientSideModelGenerator.GetModelAsync(new XtraReport(), dataSources, "/DXXRD", "/DXXRDV", "/DXXQB") :
                  await designerClientSideModelGenerator.GetModelAsync(reportUrl, dataSources, "/DXXRD", "/DXXRDV", "/DXXQB");
              model.WizardSettings.EnableSqlDataSource = true;
              model.Assign(designerModelSettings);
              var modelJsonScript = designerClientSideModelGenerator.GetJsonModelScript(model);
              return Content(modelJsonScript, "application/json");
          }
      }
      
      public class CustomQueryBuilderController : QueryBuilderController {
          public CustomQueryBuilderController(IQueryBuilderMvcControllerService controllerService)
              : base(controllerService) {    }
      }
      

    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: Run the Installation Wizard - DevExpress Unified Component Installer.

    1. Switch to the BlazorWasmReportingApp project. Select Project -> Add New Item… to invoke the Add New Item dialog. Navigate to the Reporting node and select the DevExpress v.25.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.

      Report Wizard New Blank Report

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

      Edit a Report in the VS Designer

    4. Click the report’s smart tag and select Save…:

      Save New Report

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

    Implement a Report Storage

    1. Implement the ReportStorageWebExtension descendant to save and load reports.

      Important

      This tutorial uses the file system to store reports for demonstration purposes only. In a production application, use a database or other secure storage to save reports. For more information, refer to the following help topic: Add a Report Storage.

      The following code is a sample storage that uses the file system to store reports. Add the ReportStorage.cs class file to your server project with following content:

      using DevExpress.XtraReports.UI;
      
      public class ReportStorage : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension {
          readonly string ReportDirectory;
          const string FileExtension = ".repx";
      
          public ReportStorage(IWebHostEnvironment env) {
              ReportDirectory = Path.Combine(env.ContentRootPath, "Reports");
              if (!Directory.Exists(ReportDirectory)) {
                  Directory.CreateDirectory(ReportDirectory);
              }
          }
      
          public override bool CanSetData(string url) {
              // Determines whether or not 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 (i.e., if the IsValidUrl method returned true) before the SetData method is called.
      
              return true;
          }
      
          public override bool IsValidUrl(string url) {
              // Determines whether or not the URL passed to the current Report Storage is valid. 
              // For instance, implement your own logic to prohibit URLs that contain white spaces or some 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));
                  }
                  if (ReportFactory.Reports.ContainsKey(url)) {
                      using (MemoryStream ms = new MemoryStream()) {
                          ReportFactory.Reports[url]().SaveLayoutToXml(ms);
                          return ms.ToArray();
                      }
                  }
              }
              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 a storage.
      
              return Directory.GetFiles(ReportDirectory, "*" + FileExtension)
                                      .Select(Path.GetFileNameWithoutExtension)
                                      .Union(ReportFactory.Reports.Select(x => x.Key))
                                      .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.
              var resolvedUrl = Path.GetFullPath(Path.Combine(ReportDirectory, url + FileExtension));
              if (!resolvedUrl.StartsWith(ReportDirectory + Path.DirectorySeparatorChar)) {
                  throw new DevExpress.XtraReports.Web.ClientControls.FaultException("Invalid report name.");
              }
      
              report.SaveLayoutToXml(resolvedUrl);
          }
      
          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;
          }
      }
      
    2. Add the ReportFactory.cs class file to the server project with the following code:

      using DevExpress.XtraReports.UI;
      
      public static class ReportFactory {
          public static Dictionary<string, Func<XtraReport>> Reports = new Dictionary<string, Func<XtraReport>>() {
              ["TestReport"] = () => new TestReport()
          };
      }
      
    3. In the Program.cs file, register the ReportStorage service:

      using BlazorWasmReportingApp;
      using DevExpress.AspNetCore;
      using DevExpress.AspNetCore.Reporting;
      using DevExpress.XtraReports.Web.Extensions;
      
      var builder = WebApplication.CreateBuilder(args);
      
      // Add services to the container.
      builder.Services.AddControllersWithViews();
      builder.Services.AddRazorPages();
      builder.Services.AddDevExpressControls();
      // Register the service that works for the Document Viewer (name resolution)
      // and Report Designer (loads and saves reports).
      builder.Services.AddScoped<ReportStorageWebExtension, ReportStorage>();
      
      //...
      

    Create a Page With the Report Designer

    In the BlazorWasmReportingApp.Client/Pages folder, create a new Razor component (ReportDesigner.razor) with the following content:

    @page "/designer"
    @using DevExpress.Blazor.Reporting
    @rendermode InteractiveWebAssembly
    
    <DxWasmReportDesigner ReportName="TestReport" Height="700px" Width="100%" DevelopmentMode=true>
        <DxWasmReportDesignerRequestOptions GetDesignerModelAction="DXXRD/GetReportDesignerModel">
        </DxWasmReportDesignerRequestOptions>
    </DxWasmReportDesigner>
    

    The DxWasmReportDesigner component loads the TestReport report.

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

    <div class="nav-item px-3">
        <NavLink class="nav-link" href="designer">
            <span class="oi oi-list-rich" aria-hidden="true"></span> Designer
        </NavLink>
    </div>
    

    Run the Project

    Run the project and see the result. The Report Designer displays the TestReport:

    Report Designer

    Next Steps

    Use Data Sources
    Learn how to use data sources in the JavaScript-based Report Designer.
    Customize the Report Designer UI and Behavior

    Refer to the following topics to learn how to customize the Report Designer UI and behavior:

    Troubleshooting
    This topic lists common issues that can occur in a Web Reporting application and describes solutions. For information on how to identify the cause of an issue, refer to the following topic: Reporting Application Diagnostics.