Custom Functions in the Expression Editor (ASP.NET Web Forms)
- 4 minutes to read
This topic describes how to add a custom function to the Expression Editor or remove a function from the list of available functions.
Add a Custom Function
Implement a Custom Function
To create a custom function, create an object that descends from the ReportCustomFunctionOperatorBase abstract class.
The following code defines a custom function CustomFormatFunction(string format, object arg0)
:
#region #usings
using DevExpress.XtraReports.Expressions;
using System;
#endregion
namespace CustomFunctionInExpressionAspNetCore.Services
{
#region #CustomFormatFunction
public class CustomFormatFunction : ReportCustomFunctionOperatorBase
{
public override string FunctionCategory
=> "Custom";
public override string Description
=> "CustomFormatFunction(string format, object arg0)" +
"\r\nConverts an arg0 value to a string based on a specified format";
public override bool IsValidOperandCount(int count)
=> count == 2;
public override bool IsValidOperandType(int operandIndex, int operandCount, Type type)
=> true;
public override int MaxOperandCount
=> 2;
public override int MinOperandCount
=> 2;
public override object Evaluate(params object[] operands)
{
string res = String.Format(operands[0].ToString(), operands[1]);
return res;
}
public override string Name
=> "CustomFormatFunction";
public override Type ResultType(params Type[] operands)
{
return typeof(string);
}
}
#endregion
}
The following code defines a custom function [].CountDistinct(object arg0)
:
using DevExpress.Data.Filtering;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace CustomFunctionInExpressionAspNetCore.Services {
class CountDistinctCustomAggregate : ICustomAggregateFormattable, ICustomAggregateBrowsable {
static readonly CountDistinctCustomAggregate instance = new CountDistinctCustomAggregate();
public static void Register() {
CriteriaOperator.RegisterCustomAggregate(instance);
}
public static void Unregister() {
CriteriaOperator.UnregisterCustomAggregate(instance);
}
public string Name { get { return nameof(CountDistinct); } }
public int MinOperandCount => -1;
public int MaxOperandCount => -1;
public string Description => "This is a custom aggregate function";
Type ICustomAggregate.ResultType(params Type[] operands) {
return typeof(int);
}
object ICustomAggregate.CreateEvaluationContext() {
return new HashSet<object>();
}
bool ICustomAggregate.Process(object context, object[] operands) {
var ctx = (HashSet<object>)context;
ctx.Add(operands[0]);
return false;
}
object ICustomAggregate.GetResult(object context) {
var ctx = (HashSet<object>)context;
return ctx.Count;
}
string ICustomAggregateFormattable.Format(Type providerType, params string[] operands) {
return string.Format("COUNT(distinct {0})", operands[0]);
}
public static object CountDistinct<T>(IEnumerable<T> collection, Expression<Func<T, object>> arg) {
throw new InvalidOperationException("This method should not be called explicitly.");
}
public bool IsValidOperandCount(int count) {
return true;
}
public bool IsValidOperandType(int operandIndex, int operandCount, Type type) {
return true;
}
}
}
Register a Function on the Server
Call the CustomFunctions.Register method at application startup:
public class Startup {
//...
public void ConfigureServices(IServiceCollection services) {
// ...
services.ConfigureReportingServices(configurator => {
configurator.ConfigureReportDesigner(designerConfigurator => {
//...
DevExpress.XtraReports.Expressions.CustomFunctions.Register(new CustomFormatFunction());
});
//...
});
//...
}
//...
}
Register a Function on the Client
Handle the BeforeRender event and use the following code to add the CustomFormatFunction
to the list of available functions in the Expression Editor:
function OnBeforeRender(event) {
DevExpress.Analytics.Localization.loadMessages({
"CustomStringId.CustomFormatFunction":
"CustomFormatFunction(string format, object arg0)\r\n" +
"Converts an arg0 value to a string based on the specified format.",
"CustomStringId.CountDistinctCustomAggregate":
"[].CountDistinct(object arg0)\r\n" +
"Counts the number of distinct values in the specified collection."
});
DevExpress.Reporting.Designer.Widgets.reportFunctionDisplay.push({
display: "Custom",
items: {
CustomFormatFunction: [{
paramCount: 2,
text: "CustomFormatFunction('{0:C}', 0)",
displayName: "CustomFormatFunction(format, arg0)",
descriptionStringId: "CustomStringId.CustomFormatFunction"
}]
}
});
var aggregate = DevExpress.Reporting.Designer.Widgets.reportFunctionDisplay.filter(x => x.category === 'Aggregate')[0];
aggregate.items['CountDistinct'] = [{
paramCount: 1,
text: '[].CountDistinct()',
displayName: 'CountDistinct()',
descriptionStringId: `CustomStringId.CountDistinctCustomAggregate`}];
delete DevExpress.Reporting.Designer.Widgets.reportFunctionDisplay[1].items["LocalDateTimeThisYear"];
}
The code uses the ReportCustomFunctionOperatorBase.Name property value as the name of the function to register.
Result
The following image shows a custom function (CustomFormatFunction
) in the list of available functions in the Expression Editor:
Remove a Function from the List of Available Functions
Handle the BeforeRender event and remove a function from the DevExpress.Reporting.Designer.Widgets.reportFunctionDisplay collection. To get access to a function, use the following notation:
- DevExpress.Reporting.Designer.Widgets.reportFunctionDisplay[index].items[“function_name”]
- where index is a function category index.
The following code removes the LocalDateTimeThisYear function from the DateTime category:
function OnBeforeRender(event) {
<!-- ... -->
delete DevExpress.Reporting.Designer.Widgets.reportFunctionDisplay[1].items["LocalDateTimeThisYear"];
}
Unregister a Function
If you remove a function from the DevExpress.Reporting.Designer.Widgets.reportFunctionDisplay collection on the client, the function is still registered and expressions with that function remain valid and can be evaluated. You can type the function name and compose a new expression manually.
If you have registered a custom function, you can call the CustomFunctions.Unregister method at application startup to unregister it:
void Application_Start(object sender, EventArgs e) {
// ...
DevExpress.XtraReports.Expressions.CustomFunctions.Unregister("CustomFormatFunction");
}
After this code is executed, the CustomFormatFunction
function is not available in the Expression Editor and won’t be evaluated in expressions.