Custom Function Based Filters
- 11 minutes to read
Overview
You can display a filter based on a custom function in pop-up filter menus and filter editors. For example, the following image shows Black Friday Discount filter based on the corresponding custom function:
Note
Run the XtraGrid demo to try out the following custom function-based filters:
- Is Black Friday Discount filter in the Discount column’s pop-up filter menu and filter editor.
- Is Weekend filter in the Sales Date column’s pop-up filter menu and filter editor.
- Not Begins With filter in the Name column’s filter editor.
Custom Function Operands
Filters can be based on a custom function that accepts one (unary function) or two (binary function) operands:
- the first operand - a field value being processed
- the second operand - a value against which field values are evaluated (users specify the second operand in the filter editor)
The filter editor supports binary and unary functions; the pop-up filter menus support unary functions only.
How to Implement and Display a Custom Function Based Filter
To create a custom function that can be displayed as a filter, implement the ICustomFunctionDisplayAttributes interface. It extends the ICustomFunctionOperatorBrowsable interface with the following properties:
- DisplayName — a string value that specifies the filter’s caption.
- Image — an Image, SvgImage or image URI that specifies the filter’s glyph.
Tip
In v22.1 and newer, you can implement the extended version of this interface: ICustomFunctionDisplayAttributesEx. Its GetOperandDisplayValue
method allows you to set a public name for filter operands. For instance, the following sample code adds the word “days” to the operand value (for instance, “3 days” instead of “3”).
Next, use the CriteriaOperator.RegisterCustomFunction or CriteriaOperator.RegisterCustomFunctions method to register the implemented function.
Do one of the following to display the corresponding filter in filter menus and the filter editor.
Use Data Annotation Attributes
For a data control bound to a Code First data source, annotate the data field with the CustomFunction attribute.
[CustomFunction(IsBlackFridayDiscountFunction.FunctionName)]
public double Discount { get; set; }
Handle Events
- For all DevExpress controls in the application — the static (Shared in VB) CriteriaOperator.QueryCustomFunctions event.
- For the grid and its filter editor — the ColumnView.QueryCustomFunctions event.
- For the tree list and its filter editor — the TreeList.QueryCustomFunctions event.
- For the vertical grid and its filter editor — the VGridControl.QueryCustomFunctions event.
- For the pivot grid and its filter editor — the PivotGridControl.QueryCustomFunctions event.
- For the stand-alone filter editor — the FilterControl.QueryCustomFunctions event.
- For the advanced stand-alone filter editor — the FilterEditorControl.QueryCustomFunctions event.
Unary Functions
Unary functions accept the single operand - the field value. Unary function-based filters do not allow users to specify a parameter.
The code below shows how to implement Black Friday Discount function. This is a unary function that evaluates to true if the operand is more than or equals 0.15.
Note
Run the XtraGrid demo and click Open Solution for the complete example.
using DevExpress.Data.Filtering;
IsBlackFridayDiscountFunction.Register();
gridView1.QueryCustomFunctions += OnQueryCustomFunctions;
void OnQueryCustomFunctions(object sender, Data.Filtering.CustomFunctionEventArgs e) {
if(e.PropertyName == "Discount")
e.Add(IsBlackFridayDiscountFunction.FunctionName);
}
public class IsBlackFridayDiscountFunction : ICustomFunctionDisplayAttributes {
public const string FunctionName = "IsBlackFridayDiscount";
static readonly IsBlackFridayDiscountFunction Instance = new IsBlackFridayDiscountFunction();
IsBlackFridayDiscountFunction() { }
public static void Register() {
CriteriaOperator.RegisterCustomFunction(Instance);
}
public static bool Unregister() {
return CriteriaOperator.UnregisterCustomFunction(Instance);
}
#region ICustomFunctionOperatorBrowsable Members
public FunctionCategory Category {
get { return FunctionCategory.Math; }
}
public string Description {
get { return "The discount amount is 15% or more."; }
}
public bool IsValidOperandCount(int count) {
return count == 1;
}
public bool IsValidOperandType(int operandIndex, int operandCount, Type type) {
return DevExpress.Data.Summary.SummaryItemTypeHelper.IsNumericalType(type);
}
public int MaxOperandCount {
get { return 1; }
}
public int MinOperandCount {
get { return 1; }
}
#endregion
#region ICustomFunctionDisplayAttributes
public string DisplayName {
get { return "Is Black Friday Discount"; }
}
public object Image {
get { return "bo_price"; }
}
#endregion
#region ICustomFunctionOperator Members
//The single operand (operands[0]) is the field value being processed.
public object Evaluate(params object[] operands) {
double discount = Convert.ToDouble(operands[0]);
return discount >= 0.15;
}
public string Name {
get { return FunctionName; }
}
public Type ResultType(params Type[] operands) {
return typeof(bool);
}
#endregion
}
Binary Functions
Binary functions accept the second operand - a user-specified parameter. Binary function-based filters can only be displayed in the filter editor.
The code below shows how to implement Not Begins With function. This is a binary function that evaluates to true if the first operand (the field value) not starts with the second operand (the value specified in the filter editor).
Note
Run the XtraGrid demo and click Open Solution for the complete example.
using DevExpress.Data.Filtering;
NotBeginsWithFunction.Register();
CriteriaOperator.QueryCustomFunctions += OnQueryCustomUIFunctions;
static void OnQueryCustomUIFunctions(object sender, CustomFunctionEventArgs e) {
if(e.PropertyType == typeof(string))
e.Add(NotBeginsWithFunction.FunctionName);
}
public class NotBeginsWithFunction : ICustomFunctionDisplayAttributes {
public const string FunctionName = "NotBeginsWith";
static readonly NotBeginsWithFunction Instance = new NotBeginsWithFunction();
NotBeginsWithFunction() { }
//
public static void Register() {
CriteriaOperator.RegisterCustomFunction(Instance);
}
public static bool Unregister() {
return CriteriaOperator.UnregisterCustomFunction(Instance);
}
#region ICustomFunctionOperatorBrowsable Members
public FunctionCategory Category {
get { return FunctionCategory.Text; }
}
public string Description {
get { return "Selects items that do not start with the specified string."; }
}
public bool IsValidOperandCount(int count) {
return count == 2;
}
public bool IsValidOperandType(int operandIndex, int operandCount, Type type) {
return type == typeof(string);
}
public int MaxOperandCount {
get { return 2; }
}
public int MinOperandCount {
get { return 2; }
}
#endregion
#region ICustomFunctionDisplayAttributes
public string DisplayName {
get { return "Not Begins With"; }
}
public object Image {
get { return null; }
}
#endregion
#region ICustomFunctionOperator Members
//The first operand (operands[0]) is the field value being processed.
//The second operand (operands[1]) is the value specified in the filter editor.
public object Evaluate(params object[] operands) {
if(operands[0] != null && operands[1] != null) {
string str1 = operands[0].ToString(); string str2 = operands[1].ToString();
return !str1.StartsWith(str2, StringComparison.InvariantCultureIgnoreCase);
}
return false;
}
public string Name {
get { return FunctionName; }
}
public Type ResultType(params Type[] operands) {
return typeof(bool);
}
#endregion
}
Example - How to Create a Custom Date-Time Filter
This example shows how to create a custom function ICustomFunctionOperator
to filter dates starting from today plus a specified number of days (14 by default).
using DevExpress.Data.Filtering;
public partial class Form1 : DevExpress.XtraEditors.XtraForm {
// ...
private void Form1_Load(object sender, EventArgs e) {
IsTodayPlusNDaysFunction.Register();
gridView1.QueryCustomFunctions += GridView1_QueryCustomFunctions;
}
private void GridView1_QueryCustomFunctions(object sender, DevExpress.XtraGrid.Views.Grid.CustomFunctionEventArgs e) {
if(e.PropertyName == "DateTime")
e.Add(IsTodayPlusNDaysFunction.FunctionName);
}
}
public class IsTodayPlusNDaysFunction : ICustomFunctionDisplayAttributes {
public const string FunctionName = "IsTodayPlusNDaysFunction";
static readonly IsTodayPlusNDaysFunction Instance = new IsTodayPlusNDaysFunction();
public static int DaysCount { get; set; } = 14;
IsTodayPlusNDaysFunction() { }
public static void Register() {
CriteriaOperator.RegisterCustomFunction(Instance);
}
public static bool Unregister() {
return CriteriaOperator.UnregisterCustomFunction(Instance);
}
#region ICustomFunctionOperatorBrowsable Members
public FunctionCategory Category {
get { return FunctionCategory.DateTime; }
}
public string Description {
get { return String.Concat("Today plus ", DaysCount, "Days"); }
}
public bool IsValidOperandCount(int count) {
return count == 1;
}
public bool IsValidOperandType(int operandIndex, int operandCount, Type type) {
return DevExpress.Data.Summary.SummaryItemTypeHelper.IsDateTime(type);
}
public int MaxOperandCount {
get { return 1; }
}
public int MinOperandCount {
get { return 1; }
}
#endregion
#region ICustomFunctionDisplayAttributes
public string DisplayName {
get { return String.Concat("Today +", DaysCount, " Days"); }
}
public object Image {
get { return null; }
}
#endregion
#region ICustomFunctionOperator Members
public object Evaluate(params object[] operands) {
DateTime dateTime = Convert.ToDateTime(operands[0]);
DateTime from = DateTime.Today;
DateTime to = from.AddDays(DaysCount).AddHours(23).AddMinutes(59).AddSeconds(59);
return (from <= dateTime && dateTime <= to);
}
public string Name {
get { return FunctionName; }
}
public Type ResultType(params Type[] operands) {
return typeof(bool);
}
#endregion
}