For more information on the capabilities outlined in this example (or if you are new to DevExpress Reports), please review the following blog post for important background information: DevExpress Web Report Designer — Custom Report Templates within the Report Wizard, Data Source UI Enhancements (v22.2)
DevExpress Reports ships with an easy-to-use/flexible Report Wizard. You can use the Report Wizard to generate reports yourself or make it available to end users (minimize development costs/maximize productivity). This example will show you how to add a custom report template to the Report Wizard and make modifications to a wizard-generated report. Our sample project includes separate projects for ASP.NET MVC, ASP.NET Core, and Angular client application with an ASP.NET Core backend.
To customize the DevExpress Report Wizard and add new wizard report templates (this example adds Instant Report and Custom Label Report templates to our default template list), you must:
- Implement a descendant of the ReportWizardCustomizationService class and register it as a service.
- Handle the Report Designer
CustomizeWizard
event to register a custom wizard page. (thereportWizardCustomization.js
file contains JavaScript code required for registration).
By customizing our wizard’s default template set, you can address a variety of usage scenarios. To see what’s possible, execute the app, invoke the Report Wizard, and select a custom template included with this demo (Instant Report
and Custom Label Report
).
To hide data source action from our Report Designer’s Field List panel, you must:
- Use the following class: ReportDesignerDataSourceSettings.
Note
To help illustrate available options, the Report Designer was configured to hide data source actions. As such, users cannot add, modify, or delete the data source used for this report.
Once you generate a wizard-based report, switch to the Field List panel. Notice that data source actions have been hidden (via ReportDesignerDataSourceSettings).
Files to Review
Service that Customizes the Report Wizard
Service Registration
- ASP.NET MVC: Global.asax.cs
- ASP.NET Core: Startup.cs
Custom Wizard Page for the Custom Label Report
- ASP.NET MVC: Designer.cshtml
- ASP.NET Core: Designer.cshtml
- Angular: report-designer.html andreport-designer.ts
- reportWizardCustomization.js
- LabelReport.cs
Report Designer Data Source Settings
- ASP.NET MVC: Designer.cshtml
- ASP.NET Core: HomeController.cs
- Angular: report-designer.html
Documentation
- ReportWizardCustomizationService
- ReportDesignerDataSourceSettings
- Customize the Report/Data Source Wizard (ASP.NET MVC)
- Customize the Report Wizard and Data Source Wizard (ASP.NET Core)
More Examples
Does this example address your development requirements/objectives?
(you will be redirected to DevExpress.com to submit your response)
Example Code
C#using DevExpress.XtraReports.UI;
using DevExpress.XtraReports.Web.ReportDesigner.DataContracts;
using DevExpress.XtraReports.Web.ReportDesigner.Services;
using DevExpress.XtraReports.Wizards;
using ReportWizardCustomizationServiceMvcExample.PredefinedReports;
using System;
using System.Linq;
using System.Threading.Tasks;
public class InstantReportWizardCustomizationService : ReportWizardCustomizationService {
public enum CustomReportType {
CustomLabelReport,
InstantReport
}
public override XtraReport TryCreateCustomReport(XtraReportModel model, object dataSource, string dataMember, CustomWizardData customWizardData, XtraReport report) {
if (Enum.TryParse(customWizardData.ReportTemplateID, out CustomReportType customReportType)) {
if (customReportType == CustomReportType.InstantReport) {
return new InstantReport() {
Name = customWizardData.ReportTemplateID,
DisplayName = customWizardData.ReportTemplateID
};
}
else if (customReportType == CustomReportType.CustomLabelReport) {
var labelReport = new LabelReport();
labelReport.ApplyCustomData(customWizardData.Data);
return labelReport;
}
}
return base.TryCreateCustomReport(model, dataSource, dataMember, customWizardData, report);
}
public override Task<XtraReport> TryCreateCustomReportAsync(XtraReportModel model, object dataSource, string dataMember, CustomWizardData customWizardData, XtraReport report) {
return Task.FromResult(TryCreateCustomReport(model, dataSource, dataMember, customWizardData, report));
}
public override void CustomizeReportTypeList(ReportWizardTemplateCollection predefinedTypes) {
predefinedTypes.Remove(predefinedTypes.Where(x => x.ID == nameof(ReportType.CrossTab)).First());
predefinedTypes.Add(new DevExpress.XtraReports.Web.ReportDesigner.DataContracts.ReportWizardTemplate() {
CanInstantlyFinish = true,
ID = nameof(CustomReportType.InstantReport),
Text = "Instant Report",
ImageTemplateName = "instant-report"
});
predefinedTypes.Add(new DevExpress.XtraReports.Web.ReportDesigner.DataContracts.ReportWizardTemplate() {
CanInstantlyFinish = true,
ID = nameof(CustomReportType.InstantReport),
Text = "Instant Report",
ImageClassName = "instant-report-image"
});
predefinedTypes.Add(new DevExpress.XtraReports.Web.ReportDesigner.DataContracts.ReportWizardTemplate() {
ID = nameof(CustomReportType.CustomLabelReport),
Text = "Custom Label Report",
ImageTemplateName = "dxrd-svg-wizard-LabelReport"
});
}
public override Task CustomizeReportTypeListAsync(ReportWizardTemplateCollection predefinedTypes) {
CustomizeReportTypeList(predefinedTypes);
return Task.CompletedTask;
}
public override void CustomizeReportOnFinish(XtraReport report) {
if (report.Bands.GetBandByType(typeof(ReportHeaderBand)) == null)
report.Bands.Add(new ReportHeaderBand() {
Controls = {
new XRLabel() {
Text = string.Format("Instant Report {0:MMM dd}", System.DateTime.Today),
SizeF= new System.Drawing.SizeF(650F, 100F),
Font = new DevExpress.Drawing.DXFont("Arial", 24)
} }
}); ; ;
}
public override Task CustomizeReportOnFinishAsync(XtraReport report) {
CustomizeReportOnFinish(report);
return Task.CompletedTask;
}
}
C#using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using DevExpress.Web.Mvc;
using ReportWizardCustomizationServiceMvcExample.Services;
using DevExpress.XtraReports.Web.ReportDesigner.Services;
using DevExpress.XtraReports.Web.ReportDesigner;
namespace ReportWizardCustomizationServiceMvcExample {
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication {
protected void Application_Start() {
DevExpress.XtraReports.Configuration.Settings.Default.UserDesignerOptions.DataBindingMode = DevExpress.XtraReports.UI.DataBindingMode.Expressions;
DevExpress.XtraReports.Web.WebDocumentViewer.Native.WebDocumentViewerBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Required;
DevExpress.XtraReports.Web.QueryBuilder.Native.QueryBuilderBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Required;
DevExpress.XtraReports.Web.ReportDesigner.Native.ReportDesignerBootstrapper.SessionState = System.Web.SessionState.SessionStateBehavior.Required;
DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension.RegisterExtensionGlobal(new CustomReportStorageWebExtension(Server.MapPath("/Reports")));
DevExpress.XtraReports.Web.ReportDesigner.DefaultReportDesignerContainer.RegisterDataSourceWizardConnectionStringsProvider<CustomDataSourceWizardConnectionStringsProvider>();
DevExpress.XtraReports.Web.ReportDesigner.DefaultReportDesignerContainer.RegisterDataSourceWizardJsonConnectionStorage<CustomDataSourceWizardJsonDataConnectionStorage>(true);
DevExpress.XtraReports.Web.WebDocumentViewer.DefaultWebDocumentViewerContainer.Register<DevExpress.DataAccess.Web.IJsonDataConnectionProviderFactory, CustomJsonDataConnectionProviderFactory>();
DefaultReportDesignerContainer.Register<ReportWizardCustomizationService, InstantReportWizardCustomizationService>();
DevExpress.XtraReports.Web.ReportDesigner.DefaultReportDesignerContainer.RegisterObjectDataSourceWizardTypeProvider<ObjectDataSourceWizardCustomTypeProvider>();
System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12;
MVCxReportDesigner.StaticInitialize();
DevExpress.XtraReports.Web.ClientControls.LoggerService.Initialize((ex, message) => System.Diagnostics.Debug.WriteLine("[{0}]: Exception occurred. Message: '{1}'. Exception Details:\r\n{2}", DateTime.Now, message, ex));
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.DefaultBinder = new DevExpress.Web.Mvc.DevExpressEditorsBinder();
DevExpress.Web.ASPxWebControl.CallbackError += Application_Error;
}
protected void Application_Error(object sender, EventArgs e) {
Exception exception = System.Web.HttpContext.Current.Server.GetLastError();
//TODO: Handle Exception
}
}
}
C#using System;
using System.IO;
using DevExpress.AspNetCore;
using DevExpress.AspNetCore.Reporting;
using DevExpress.Security.Resources;
using DevExpress.XtraReports.Web.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ReportWizardCustomizationServiceAspNetCoreExample.Services;
using ReportWizardCustomizationServiceAspNetCoreExample.Data;
using DevExpress.XtraReports.Web.ReportDesigner.Services;
namespace ReportWizardCustomizationServiceAspNetCoreExample {
public class Startup {
public Startup(IConfiguration configuration, IWebHostEnvironment hostingEnvironment) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
services.AddDevExpressControls();
services.AddScoped<ReportStorageWebExtension, CustomReportStorageWebExtension>();
services
.AddMvc()
.AddNewtonsoftJson();
services.ConfigureReportingServices(configurator => {
configurator.ConfigureReportDesigner(designerConfigurator => {
designerConfigurator.RegisterDataSourceWizardConnectionStringsProvider<CustomSqlDataSourceWizardConnectionStringsProvider>();
designerConfigurator.RegisterDataSourceWizardJsonConnectionStorage<CustomDataSourceWizardJsonDataConnectionStorage>(true);
designerConfigurator.RegisterObjectDataSourceWizardTypeProvider<ObjectDataSourceWizardCustomTypeProvider>();
});
configurator.ConfigureWebDocumentViewer(viewerConfigurator => {
viewerConfigurator.UseCachedReportSourceBuilder();
viewerConfigurator.RegisterJsonDataConnectionProviderFactory<CustomJsonDataConnectionProviderFactory>();
viewerConfigurator.RegisterConnectionProviderFactory<CustomSqlDataConnectionProviderFactory>();
});
configurator.UseAsyncEngine();
});
services.AddDbContext<ReportDbContext>(options => options.UseSqlite(Configuration.GetConnectionString("ReportsDataConnectionString")));
services.AddScoped<ReportWizardCustomizationService, InstantReportWizardCustomizationService>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory, ReportDbContext db) {
db.InitializeDatabase();
var contentDirectoryAllowRule = DirectoryAccessRule.Allow(new DirectoryInfo(Path.Combine(env.ContentRootPath, "..", "Content")).FullName);
AccessSettings.ReportingSpecificResources.TrySetRules(contentDirectoryAllowRule, UrlAccessRule.Allow());
DevExpress.XtraReports.Configuration.Settings.Default.UserDesignerOptions.DataBindingMode = DevExpress.XtraReports.UI.DataBindingMode.Expressions;
app.UseDevExpressControls();
System.Net.ServicePointManager.SecurityProtocol |= System.Net.SecurityProtocolType.Tls12;
if(env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
} else {
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Designer}/{id?}");
});
}
}
}
Razor@using ReportWizardCustomizationServiceMvcExample.Models
@model ReportDesignerModel
<script type="text/html" id="instant-report">
<svg viewBox="-2 -5 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_1" transform="translate(-2, -5)" style="enable-background:new 0 0 32 32">
<g id="Check">
<polygon class="dxd-icon-fill" points="27,5 11,21 5,15 2,18 11,27 30,8 " fill="#727272" />
</g>
</g>
</svg>
</script>
<script type="text/html" id="wizard-labels-page">
<div class="dxrd-wizard-type-page dxrd-wizard-type-page-labels">
<div class="page-title">Custom Wizard Page</div>
<div class="dx-fieldset">
<div class="dx-field">
<div class="dx-field-label">Report Name</div>
<div class="dx-field-value">
<div data-bind="dxTextBox: { value: label4Value }"></div>
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Label1: </div>
<div class="dx-field-value">
<div data-bind="dxTextBox: { value: label1Value }"></div>
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Label2: </div>
<div class="dx-field-value">
<div data-bind="dxTextBox: { value: label2Value }"></div>
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Label3: </div>
<div class="dx-field-value">
<div data-bind="dxTextBox: { value: label3Value }"></div>
</div>
</div>
</div>
</div>
</script>
<link href="~/Content/reportWizardCustomization.css" rel="stylesheet" />
<script src="~/Scripts/reportWizardCustomization.js"></script>
@Html.DevExpress().ReportDesigner(settings => {
settings.Name = "ReportDesigner1";
settings.ClientSideEvents.CustomizeWizard = "CustomizeReportWizard";
settings.SettingsDataSource.AllowAddDataSource = false;
settings.SettingsDataSource.AllowEditDataSource = false;
settings.SettingsDataSource.AllowRemoveDataSource = false;
// Add the created data source to the list of default data sources.
foreach (var dataSourceItem in Model.DataSources)
settings.DataSources.Add(dataSourceItem.Key, dataSourceItem.Value);
}).BindToUrl("TestReport").GetHtml()
Razor@using ReportWizardCustomizationServiceAspNetCoreExample.Models
@model ReportDesignerCustomModel
@{
var designerRender = Html.DevExpress().ReportDesigner("reportDesigner")
.Height("100%")
.ClientSideEvents((events) => {
events.CustomizeWizard("CustomizeReportWizard");
})
.Bind(Model.ReportDesignerModel);
@designerRender.RenderHtml()
}
@section Scripts {
<link href="~/css/dx-reporting-skeleton-screen.css" rel="stylesheet" />
<link rel="stylesheet" href="~/css/viewer.part.bundle.css" />
<link rel="stylesheet" href="~/css/designer.part.bundle.css" />
<link rel="stylesheet" href="~/css/ace/ace.bundle.css" />
<link rel="stylesheet" href="~/css/dx.material.blue.light.bundle.css" />
<script src="~/js/reporting.thirdparty.bundle.js"></script>
<script src="~/js/viewer.part.bundle.js"></script>
<script src="~/js/designer.part.bundle.js"></script>
<script src="~/js/reportWizardCustomization.js"></script>
<script type="text/html" id="instant-report">
<svg viewBox="-2 -5 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Layer_1" transform="translate(-2, -5)" style="enable-background:new 0 0 32 32">
<g id="Check">
<polygon class="dxd-icon-fill" points="27,5 11,21 5,15 2,18 11,27 30,8 " fill="#727272" />
</g>
</g>
</svg>
</script>
<script type="text/html" id="wizard-labels-page">
<div class="dxrd-wizard-type-page dxrd-wizard-type-page-labels">
<div class="page-title">Custom Wizard Page</div>
<div class="dx-fieldset">
<div class="dx-field">
<div class="dx-field-label">Report Name</div>
<div class="dx-field-value">
<div data-bind="dxTextBox: { value: label4Value }"></div>
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Label1: </div>
<div class="dx-field-value">
<div data-bind="dxTextBox: { value: label1Value }"></div>
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Label2: </div>
<div class="dx-field-value">
<div data-bind="dxTextBox: { value: label2Value }"></div>
</div>
</div>
<div class="dx-field">
<div class="dx-field-label">Label3: </div>
<div class="dx-field-value">
<div data-bind="dxTextBox: { value: label3Value }"></div>
</div>
</div>
</div>
</div>
</script>
<link href="~/css/reportWizardCustomization.css" rel="stylesheet" />
@designerRender.RenderScripts()
}
HTML<dx-report-designer [reportUrl]="reportUrl" height="calc(100vh - 90px)">
<dxrd-request-options [getDesignerModelAction]="getDesignerModelAction" [host]="hostUrl"></dxrd-request-options>
<dxrd-designer-model-settings [allowMDI]="true">
<dxrd-datasource-settings [allowAddDataSource]="false" [allowEditDataSource]="false" [allowRemoveDataSource]="false"></dxrd-datasource-settings>
</dxrd-designer-model-settings>
<dxrd-callbacks (CustomizeWizard)="OnCustomizeReportWizard($event)">
</dxrd-callbacks>
</dx-report-designer>
TypeScriptimport { Component, Inject, ViewEncapsulation } from '@angular/core';
import { WizardPageBase } from '@devexpress/analytics-core/analytics-wizard';
import { addToBindingsCache } from '@devexpress/analytics-core/analytics-utils'
import * as ko from 'knockout';
import * as $ from 'jquery';
@Component({
selector: 'report-designer',
encapsulation: ViewEncapsulation.None,
templateUrl: './report-designer.html',
styleUrls: [
'../../../node_modules/ace-builds/css/ace.css',
'../../../node_modules/ace-builds/css/theme/dreamweaver.css',
'../../../node_modules/ace-builds/css/theme/ambiance.css',
"../../../node_modules/devextreme/dist/css/dx.material.blue.light.css",
"../../../node_modules/@devexpress/analytics-core/dist/css/dx-analytics.common.css",
"../../../node_modules/@devexpress/analytics-core/dist/css/dx-analytics.material.blue.light.css",
"../../../node_modules/@devexpress/analytics-core/dist/css/dx-querybuilder.css",
"../../../node_modules/devexpress-reporting/dist/css/dx-webdocumentviewer.css",
"../../../node_modules/devexpress-reporting/dist/css/dx-reportdesigner.css"
]
})
export class ReportDesignerComponent {
getDesignerModelAction = "DXXRD/GetDesignerModel";
reportUrl = "TestReport";
OnCustomizeReportWizard(event) {
this.CustomizeReportWizard(event.args);
}
beforeInitializeWizard(args) {
class LabelPage extends WizardPageBase {
canNext() {
return false;
}
canFinish() {
return true;
}
commit() {
return $.Deferred().resolve({
Label1: this.label1Value(),
Label2: this.label2Value(),
Label3: this.label3Value(),
ReportName: this.label4Value(),
}).promise();
}
label1Value = ko.observable('Label1');
label2Value = ko.observable('Label2');
label3Value = ko.observable('Label3');
label4Value = ko.observable('SomeReportName');
}
args.wizard.pageFactory.registerMetadata('CustomLabelPage', {
create: () => new LabelPage(),
getState: (state) => state,
setState: (data, state) => {
state.customData = JSON.stringify(data);
},
resetState: (state, defaultState) => {
state.customData = undefined;
},
template: 'wizard-labels-page',
navigationPanelText: 'Specify label values'
});
}
afterInit(args) {
const defaultGetNextPageId = args.wizard.iterator.getNextPageId;
args.wizard.iterator.getNextPageId = function (pageId) {
if (pageId === 'selectReportTypePage' && args.wizard.iterator._getCurrentState().reportTemplateID === 'CustomLabelReport') {
return 'CustomLabelPage';
} else {
return defaultGetNextPageId.apply(this, [pageId]);
}
};
}
beforeFinishWizard(args) {
args.state.customData = args.state.customData || 'CustomizeEmptyReport';
}
CustomizeReportWizard(event) {
if (event.Type === 'ReportWizard') {
addToBindingsCache('dxTextBox: { value: label4Value }', function ($context, $element) { return { 'dxTextBox': function () { return { 'value': $context.$data.label4Value } } } });
addToBindingsCache('dxTextBox: { value: label1Value }', function ($context, $element) { return { 'dxTextBox': function () { return { 'value': $context.$data.label1Value } } } });
addToBindingsCache('dxTextBox: { value: label2Value }', function ($context, $element) { return { 'dxTextBox': function () { return { 'value': $context.$data.label2Value } } } });
addToBindingsCache('dxTextBox: { value: label3Value }', function ($context, $element) { return { 'dxTextBox': function () { return { 'value': $context.$data.label3Value } } } });
event.Wizard.events.addHandler('beforeInitialize', this.beforeInitializeWizard);
event.Wizard.events.addHandler('afterInitialize', this.afterInit);
event.Wizard.events.addHandler('beforeFinish', this.beforeFinishWizard);
}
}
constructor(@Inject('BASE_URL') public hostUrl: string) { }
}
JavaScriptfunction beforeInitializeWizard(args) {
class LabelPage extends DevExpress.Analytics.Wizard.WizardPageBase {
canNext() {
return false;
}
canFinish() {
return true;
}
commit() {
return $.Deferred().resolve({
Label1: this.label1Value(),
Label2: this.label2Value(),
Label3: this.label3Value(),
ReportName: this.label4Value(),
}).promise();
}
label1Value = ko.observable('Label1');
label2Value = ko.observable('Label2');
label3Value = ko.observable('Label3');
label4Value = ko.observable('SomeReportName');
}
args.wizard.pageFactory.registerMetadata('CustomLabelPage', {
create: () => new LabelPage(),
getState: (state) => state,
setState: (data, state) => {
state.customData = JSON.stringify(data);
},
resetState: (state, defaultState) => {
state.customData = undefined;
},
template: 'wizard-labels-page',
navigationPanelText: 'Specify label values'
});
}
function afterInit(args) {
const defaultGetNextPageId = args.wizard.iterator.getNextPageId;
args.wizard.iterator.getNextPageId = function (pageId) {
if(pageId === 'selectReportTypePage' && args.wizard.iterator._getCurrentState().reportTemplateID === 'CustomLabelReport') {
return 'CustomLabelPage';
} else {
return defaultGetNextPageId.apply(this, [pageId]);
}
};
}
function CustomizeReportWizard(s, e) {
if(e.Type === 'ReportWizard') {
DevExpress.Analytics.Widgets.Internal.addToBindingsCache('dxTextBox: { value: label4Value }', function($context, $element) { return { 'dxTextBox': function() { return { 'value': $context.$data.label4Value } } } });
DevExpress.Analytics.Widgets.Internal.addToBindingsCache('dxTextBox: { value: label1Value }', function($context, $element) { return { 'dxTextBox': function() { return { 'value': $context.$data.label1Value } } } });
DevExpress.Analytics.Widgets.Internal.addToBindingsCache('dxTextBox: { value: label2Value }', function($context, $element) { return { 'dxTextBox': function() { return { 'value': $context.$data.label2Value } } } });
DevExpress.Analytics.Widgets.Internal.addToBindingsCache('dxTextBox: { value: label3Value }', function($context, $element) { return { 'dxTextBox': function() { return { 'value': $context.$data.label3Value } } } });
e.Wizard.events.addHandler('beforeInitialize', beforeInitializeWizard);
e.Wizard.events.addHandler('afterInitialize', afterInit);
e.Wizard.events.addHandler('beforeFinish', (result) => {
result.state.customData = result.state.customData || 'CustomizeEmptyReport';
});
}
}
C#using System.Text.Json;
namespace ReportWizardCustomizationServiceMvcExample.PredefinedReports {
public partial class LabelReport : DevExpress.XtraReports.UI.XtraReport {
public LabelReport() {
InitializeComponent();
}
public void ApplyCustomData(string customDataJson) {
Labels labels = JsonSerializer.Deserialize<Labels>(customDataJson);
xrLabel1.Text = labels.Label1;
xrLabel2.Text = labels.Label2;
xrLabel3.Text = labels.Label3;
Name = labels.ReportName;
DisplayName = labels.ReportName;
}
}
public class Labels {
public string Label1 { get; set; }
public string Label2 { get; set; }
public string Label3 { get; set; }
public string ReportName { get; set; }
}
}
C#using System.Collections.Generic;
using System.Threading.Tasks;
using DevExpress.DataAccess.Sql;
using Microsoft.AspNetCore.Mvc;
using DevExpress.XtraReports.UI;
using DevExpress.XtraReports.Web.ReportDesigner;
using DevExpress.AspNetCore.Reporting.QueryBuilder;
using DevExpress.AspNetCore.Reporting.ReportDesigner;
using DevExpress.AspNetCore.Reporting.WebDocumentViewer;
namespace ReportWizardCustomizationServiceAspNetCoreExample.Controllers {
public class HomeController : Controller {
public IActionResult Index() {
return View();
}
public IActionResult Error() {
Models.ErrorModel model = new Models.ErrorModel();
return View(model);
}
public async Task<IActionResult> Designer(
[FromServices] IReportDesignerClientSideModelGenerator clientSideModelGenerator,
[FromQuery] string reportName) {
Models.ReportDesignerCustomModel model = new Models.ReportDesignerCustomModel();
model.ReportDesignerModel = await CreateDefaultReportDesignerModel(clientSideModelGenerator, reportName, null);
model.ReportDesignerModel.DataSourceSettings.AllowAddDataSource = false;
model.ReportDesignerModel.DataSourceSettings.AllowEditDataSource = false;
model.ReportDesignerModel.DataSourceSettings.AllowRemoveDataSource = false;
return View(model);
}
public static Dictionary<string, object> GetAvailableDataSources() {
var dataSources = new Dictionary<string, object>();
// Create a SQL data source with the specified connection string.
SqlDataSource ds = new SqlDataSource("NWindConnectionString");
// Create a SQL query to access the Products data table.
SelectQuery query = SelectQueryFluentBuilder.AddTable("Products").SelectAllColumnsFromTable().Build("Products");
ds.Queries.Add(query);
ds.RebuildResultSchema();
dataSources.Add("Northwind", ds);
return dataSources;
}
public static async Task<ReportDesignerModel> CreateDefaultReportDesignerModel(IReportDesignerClientSideModelGenerator clientSideModelGenerator, string reportName, XtraReport report) {
reportName = string.IsNullOrEmpty(reportName) ? "TestReport" : reportName;
var dataSources = GetAvailableDataSources();
if(report != null) {
return await clientSideModelGenerator.GetModelAsync(report, dataSources, ReportDesignerController.DefaultUri, WebDocumentViewerController.DefaultUri, QueryBuilderController.DefaultUri);
}
return await clientSideModelGenerator.GetModelAsync(reportName, dataSources, ReportDesignerController.DefaultUri, WebDocumentViewerController.DefaultUri, QueryBuilderController.DefaultUri);
}
}
}