Skip to main content

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:

image

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”).

public string GetOperandDisplayValue(object value, string displayText) {
    if (value is int)
        return string.Format("{0} days", value);
    return value?.ToString();
}

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

Unary Functions

Unary functions accept the single operand - the field value. Unary function-based filters do not allow users to specify a parameter.

image

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.

image

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
}

Custom Function to Filter Date-Time Values - WinForms Data Grid