Skip to main content
All docs
V23.2

Create Editors for Custom Parameter Types in an ASP.NET Core Application

  • 7 minutes to read

This topic explains how to implement a custom parameter type, add custom type parameters to a report, and create a custom editor for the Document Viewer’s Parameters Panel. The custom parameter type defines an email address.

Create a Custom Parameter Type and a Converter

Define a CustomParameterType class with the Value property. Implement a CustomParameterTypeConverter converter to display a parameter value in a document.

using System;
using System.ComponentModel;
using System.Globalization;

namespace CustomParameterEditorAspNetCoreExample
{
    [TypeConverter(typeof(CustomParameterTypeConverter))]
    public class CustomParameterType {
        public string Value { get;set; }
        public override string ToString() {
            return Value;
        }
    }

    public class CustomParameterTypeConverter : TypeConverter {
        public override object ConvertTo(ITypeDescriptorContext context, 
            CultureInfo culture, object value, Type destinationType) 
        {
            if(destinationType == typeof(string)) {
                return ((CustomParameterType)value).Value;
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
        public override bool CanConvertTo(ITypeDescriptorContext context, 
            Type destinationType) 
        {
            return destinationType == typeof(string) || 
                base.CanConvertTo(context, destinationType);
        }

        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }
        public override object ConvertFrom(ITypeDescriptorContext context, 
            CultureInfo culture, object value) 
        {
            var valueString = value as string;
            if(valueString != null) {
                return new CustomParameterType { Value = valueString };
            }
            return base.ConvertFrom(context, culture, value);
        }
    }
}

Implement a Custom Parameter Serializer

A serializer is necessary to pass data from the client to the controller on the server and store the parameter value in report definition files.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using DevExpress.XtraReports.Native;

namespace CustomParameterEditorAspNetCoreExample
{
    [TypeConverter(typeof(CustomParameterTypeConverter))]
    public class CustomDataSerializer : IDataSerializer
    {
        public const string Name = "myCustomDataSerializer";

        public bool CanDeserialize(string value, string typeName, object extensionProvider)
        {
            return typeName == typeof(CustomParameterType).FullName;
        }

        public bool CanSerialize(object data, object extensionProvider)
        {
            return data is CustomParameterType;
        }

        public object Deserialize(string value, string typeName, object extensionProvider)
        {
            if (typeName == typeof(CustomParameterType).FullName) {
                return new CustomParameterType { Value = value };
            }
            return null;
        }

        public string Serialize(object data, object extensionProvider) {
            var parameter = data as CustomParameterType;
            return parameter != null ? parameter.Value : null;
        }
    }
}

Register the Custom Parameter Serializer at Startup

Add the following code to the application startup file to register the CustomDataSerializer service in the application:

using DevExpress.XtraReports.Native;

var builder = WebApplication.CreateBuilder(args);

DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(typeof(CustomParameterType));
SerializationService.RegisterSerializer(CustomDataSerializer.Name, new CustomDataSerializer());

var app = builder.Build();

Add the Custom Parameter Type to the Report Designer

Handle the Report Designer’s BeforeRender event to add a custom parameter type (Custom Type) and specify the custom-parameter-editor HTML template as the parameter’s editor. The editor template is based on the dxTextBox editor.

@model DevExpress.XtraReports.UI.XtraReport

<script type="text/html" id="custom-parameter-editor">
    <div data-bind="dxTextBox: { value: value }, dxValidator: { validationRules: [{ type: 'email', message: 'Email is not valid.' }]}"></div>
</script>
<script type="text/javascript">
    function reportDesignerInit(sender, e) {
        var editor = { header: "custom-parameter-editor" };
        sender.AddParameterType({
            displayValue: "Custom Type",
            specifics: "custom",
            value: "@typeof(CustomParameterType).FullName",
            valueConverter: function(valueObj) { return valueObj; }
        }, editor);
    }
    function onCustomizeMenuActions(s, e) {
        var newReportAction = e.GetById(DevExpress.Reporting.Designer.Actions.ActionId.NewReport);
        if (newReportAction) {
            newReportAction.clickAction = function () {
                s.OpenReport("TemplateReport");
            }
        }
    }
</script>

@{
    var designerRender = Html.DevExpress().ReportDesigner("reportDesigner")
        .Height("1000px")
        .ClientSideEvents(configure =>
        {
            configure.BeforeRender("reportDesignerInit");
            configure.CustomizeMenuActions("onCustomizeMenuActions");
        })
        .Bind(Model);
    @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" />

    <script src="~/js/reporting.thirdparty.bundle.js"></script>
    <script src="~/js/viewer.part.bundle.js"></script>
    <script src="~/js/designer.part.bundle.js"></script>

    @designerRender.RenderScripts()
}

Create a New Report Template

You should add a custom parameter’s serializer to a report before you add the custom parameter to this report. In your application, add the serializer when the Report Designer creates a new report or opens a report for editing.

Modify a Report Designer controller to instantiate a report and specify a custom serialization service for the report:

using DevExpress.XtraReports.Native;
using Microsoft.AspNetCore.Mvc;

namespace CustomParameterEditorAspNetCoreExample.Controllers {
    public class HomeController : Controller {
        public IActionResult Index() {
            return View();
        }
        public IActionResult Error() {
            Models.ErrorModel model = new Models.ErrorModel();
            return View(model);
        }
        #region ControllerDesignerAction
        public ActionResult Designer()
        {
            var report = new DevExpress.XtraReports.UI.XtraReport();
            report.Extensions[SerializationService.Guid] = CustomDataSerializer.Name;
            return View(report);
        }
        #endregion
        public ActionResult Viewer()
        {
            return View();
        }
    }
}

Run the project and open the Report Designer. Save the empty report to a file (Template.repx). This file contains a serialized extension that references the custom type serializer. You can use the Template.repx file as a new report template.

Use Template for New Reports

All reports created with the New command in the Report Designer should be based on the custom template with the custom type serializer. Otherwise, a custom report parameter causes an error when the report is opened.

Modify the New command so that it opens a Template report:

@model DevExpress.XtraReports.UI.XtraReport

<script type="text/html" id="custom-parameter-editor">
    <div data-bind="dxTextBox: { value: value }, dxValidator: { validationRules: [{ type: 'email', message: 'Email is not valid.' }]}"></div>
</script>
<script type="text/javascript">
    function reportDesignerInit(sender, e) {
        var editor = { header: "custom-parameter-editor" };
        sender.AddParameterType({
            displayValue: "Custom Type",
            specifics: "custom",
            value: "@typeof(CustomParameterType).FullName",
            valueConverter: function(valueObj) { return valueObj; }
        }, editor);
    }
    function onCustomizeMenuActions(s, e) {
        var newReportAction = e.GetById(DevExpress.Reporting.Designer.Actions.ActionId.NewReport);
        if (newReportAction) {
            newReportAction.clickAction = function () {
                s.OpenReport("TemplateReport");
            }
        }
    }
</script>

@{
    var designerRender = Html.DevExpress().ReportDesigner("reportDesigner")
        .Height("1000px")
        .ClientSideEvents(configure =>
        {
            configure.BeforeRender("reportDesignerInit");
            configure.CustomizeMenuActions("onCustomizeMenuActions");
        })
        .Bind(Model);
    @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" />

    <script src="~/js/reporting.thirdparty.bundle.js"></script>
    <script src="~/js/viewer.part.bundle.js"></script>
    <script src="~/js/designer.part.bundle.js"></script>

    @designerRender.RenderScripts()
}

Register a Custom Serializer When Reports are Saved

The application should register a custom serializer for all reports when they are saved to avoid an error if the user adds a custom parameter to that report.

The application uses a custom report storage to save a report and open it from a file. Modify the storage’s SetData method to register the custom serializer for all saved reports:

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);
}

Create a Report with Custom Parameters

Run the application and open the Web Report Designer. Add parameters of the Custom Type to the report. This example demonstrates the following parameter variations:

  • Parameter of a custom type with a list of predefined values
  • Single parameter of a custom type
  • Multi-value parameter of a custom type
  • Multi-value parameter with a list of static lookup values

Drop parameters to the report surface to create XRLabel controls bound to parameters.

Sample Report with Custom Parameters

Create a Custom Editor in the Document Viewer

Use the WebDocumentViewerClientSideEventsBuilder.CustomizeParameterEditors method to handle the CustomizeParameterEditors client-side event and specify an editor for a custom parameter type. The editor is contained in an inline HTML template.

<script type="text/html" id="custom-parameter-text-editor">
    <div data-bind="dxTextBox: getOptions({ value: value, disabled: disabled }),
         dxValidator: { validationRules: $data.validationRules || [] }"></div>
</script>
<script type="text/javascript">
    function customizeParameterEditors(s, e) {
        if (e.parameter.type ===
            "@typeof(CustomParameterType).FullName") {
            if(!e.parameter.multiValue && !e.parameter.hasLookUpValues) {
                e.info.validationRules = e.info.validationRules || [];
                e.info.validationRules.push(
                    { type: 'email', message: 'Email parameter value has invalid format.' });
                e.info.editor = { header: "custom-parameter-text-editor" };
            }
        }
    }
</script>

@{
    var viewerRender = Html.DevExpress().WebDocumentViewer("DocumentViewer")
        .Height("1000px")
        .ClientSideEvents(configure => configure.CustomizeParameterEditors("customizeParameterEditors"))
        .Bind("CustomParameterReport");
    @viewerRender.RenderHtml()
}

@section Scripts {
    <link href="~/css/dx-reporting-skeleton-screen.css" rel="stylesheet" />
    <link rel="stylesheet" href="~/css/viewer.part.bundle.css" />
    <script src="~/js/reporting.thirdparty.bundle.js"></script>
    <script src="~/js/viewer.part.bundle.js"></script>

    @viewerRender.RenderScripts()
}

Run the Application

The Document Viewer opens the CustomParameterReport, which contains custom parameters. The Parameters panel displays custom editors for custom parameter types:

Parameters Panel with Custom Parameter Editors

This example is based on the project created from the ASP.NET Core Reporting template in our Web App Template Gallery. For more information on the Template Gallery, review the following topic: Use Visual Studio Templates to Create an ASP.NET Core Application with a Report Designer.

View Example: Custom Report Parameter Types in Web Reporting Controls (ASP.NET Core)