Example T1020323
Visible to All Users

Reporting for ASP.NET Core - Custom Report Parameter Types

This example demonstrates how to use custom types for report parameters, create custom parameter editors, and implement serialization/deserialization logic.

Files to Review

Documentation

More Examples

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

CustomParameterEditorAspNetCoreExample/Services/CustomParameterType.cs
C#
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); } } }
CustomParameterEditorAspNetCoreExample/Services/CustomDataSerializer.cs
C#
using DevExpress.XtraReports.Native; using System.ComponentModel; namespace CustomParameterEditorAspNetCoreExample { [TypeConverter(typeof(CustomParameterTypeConverter))] public class CustomDataSerializer : IDataSerializer { public const string Name = "myCustomDataSerializer"; public bool CanDeserialize(string value, string typeName, object extensionProvider) { bool canDeserialize = typeName == typeof(CustomParameterType).FullName; return canDeserialize; } 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; } } }
CustomParameterEditorAspNetCoreExample/Services/CustomReportStorageWebExtension.cs
C#
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using DevExpress.XtraReports.Web.Extensions; using DevExpress.XtraReports.UI; using Microsoft.AspNetCore.Hosting; using CustomParameterEditorAspNetCoreExample.PredefinedReports; using System.Threading.Tasks; namespace CustomParameterEditorAspNetCoreExample.Services { public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension { readonly string ReportDirectory; const string FileExtension = ".repx"; public CustomReportStorageWebExtension(IWebHostEnvironment env) { ReportDirectory = Path.Combine(env.ContentRootPath, "Reports"); if (!Directory.Exists(ReportDirectory)) { Directory.CreateDirectory(ReportDirectory); } } public override Task AfterGetDataAsync(string url, XtraReport report) { var report2 = report; return base.AfterGetDataAsync(url, report); } 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 (ReportsFactory.Reports.ContainsKey(url)) { using (MemoryStream ms = new MemoryStream()) { ReportsFactory.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(ReportsFactory.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; } } }
CustomParameterEditorAspNetCoreExample/Controllers/HomeController.cs
C#
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); } public ActionResult Designer() { var report = new DevExpress.XtraReports.UI.XtraReport(); report.Extensions[SerializationService.Guid] = CustomDataSerializer.Name; return View(report); } public ActionResult Viewer() { return View(); } } }
CustomParameterEditorAspNetCoreExample/Views/Home/Viewer.cshtml
Razor
<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() }
CustomParameterEditorAspNetCoreExample/Views/Home/Designer.cshtml
Razor
@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() }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.