This example demonstrates how to use custom types for report parameters, create custom parameter editors, and implement serialization/deserialization logic.
Files to Review
CS:
- CustomDataSerializer.cs
- CustomParameterType.cs
- CustomReportStorageWebExtension.cs
- HomeController.cs
- Viewer.cshtml
- Designer.cshtml
VB:
- CustomDataSerializer.vb
- CustomParameterType.vb
- CustomReportStorageWebExtension.vb
- HomeController.vb
- Viewer.vbhtml
- Designer.vbhtml
Documentation
- Register Custom Report Parameter Types
- Provide Custom Editors for Report Parameters
- Use Report Parameters
- XML Serialization
- Reporting — Safe Deserialization
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.Native;
namespace CustomParameterEditorAspNetMvcExample {
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;
}
}
}
C#using System;
using System.ComponentModel;
using System.Globalization;
namespace CustomParameterEditorAspNetMvcExample {
[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);
}
}
}
C#using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using DevExpress.XtraReports.Web.Extensions;
using DevExpress.XtraReports.UI;
using CustomParameterEditorAspNetMvcExample.PredefinedReports;
namespace CustomParameterEditorAspNetMvcExample.Services {
public class CustomReportStorageWebExtension : DevExpress.XtraReports.Web.Extensions.ReportStorageWebExtension {
readonly string reportDirectory;
const string FileExtension = ".repx";
public CustomReportStorageWebExtension(string reportDirectory) {
if (!Directory.Exists(reportDirectory)) {
Directory.CreateDirectory(reportDirectory);
}
this.reportDirectory = 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 (ReportsFactory.Reports.ContainsKey(url)) {
using (MemoryStream ms = new MemoryStream()) {
ReportsFactory.Reports[url]().SaveLayoutToXml(ms);
return ms.ToArray();
}
}
}
catch (Exception) {
throw new FaultException(new FaultReason("Could not get report data."), new FaultCode("Server"), "GetData");
}
throw new FaultException(new FaultReason(string.Format("Could not find report '{0}'.", url)), new FaultCode("Server"), "GetData");
}
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 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;
}
}
}
C#using DevExpress.XtraReports.Native;
using System.Web.Mvc;
namespace CustomParameterEditorAspNetMvcExample.Controllers {
public class HomeController : Controller {
public ActionResult Index() {
return View();
}
public ActionResult Designer() {
var report = new DevExpress.XtraReports.UI.XtraReport();
report.Extensions[SerializationService.Guid] = CustomDataSerializer.Name;
return View(report);
}
public ActionResult Viewer() {
return View();
}
}
}
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(CustomParameterEditorAspNetMvcExample.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>
@Html.DevExpress().WebDocumentViewer(settings =>
{
settings.Name = "WebDocumentViewer1";
settings.ClientSideEvents.CustomizeParameterEditors = "customizeParameterEditors";
}).Bind("CustomReport").GetHtml()
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(CustomParameterEditorAspNetMvcExample.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>
@Html.DevExpress().ReportDesigner(settings =>
{
settings.Name = "ReportDesigner1";
settings.ClientSideEvents.Init = "reportDesignerInit";
settings.ClientSideEvents.CustomizeMenuActions = "onCustomizeMenuActions";
}).Bind(Model).GetHtml()
Code<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 ===
"@GetType(CustomParameterEditorAspNetMvcExample.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>
@Html.DevExpress().WebDocumentViewer(
Sub(settings)
settings.Name = "WebDocumentViewer1"
settings.ClientSideEvents.CustomizeParameterEditors = "customizeParameterEditors"
End Sub).Bind("CustomReport").GetHtml()
Code@ModelType 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: "@GetType(CustomParameterEditorAspNetMvcExample.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>
@Html.DevExpress().ReportDesigner(
Sub(settings)
settings.Name = "ReportDesigner1"
settings.ClientSideEvents.Init = "reportDesignerInit"
settings.ClientSideEvents.CustomizeMenuActions = "onCustomizeMenuActions"
End Sub).Bind("Model").GetHtml()