DevExpress Winforms Controls 内置140多个UI控件和库,完美构建流畅、美观且易于使用的应用程序。想要体验?点击下载>>

DevExpress WinForms安装附带两个允许最终用户构建过滤器查询的控件:提供GUI的Filter控件和将Filter控件与基于文本输入的面板组合在一起的Filter Editor控件。WinForms中,大多数数据感知控件都使用这些组件,但是您也可以将其包含在自己的表单中,并根据需要将其绑定到数据感知控件中。

为了说明这一点,下面是带有Filter Editor的数据网格,用户可以单击过滤器面板中的Edit Filter按钮来调出Filter Editor,并且由于属性DefaultFilterEditorView设置为TextAndVisual,因此可以看到Filter Editor Control的文本面板。

winform 通用架构_winform

在下图中,您可以看到两个控件中可用的一些标准功能,包括“小于或等于”,“大于或等于”,“今天”,“昨天”以及许多其他功能。Filter控件和Filter Editor控件均提供多种功能供您选择,可用功能集因要为其构建表达式的数据字段的类型而异。

winform 通用架构_自定义函数_02

自定义函数

在某些情况下,标准函数集还不够。技术团队在处理大量技术支持问题发现,查找是最常见需要的自定义函数。以下是三种最受欢迎的方案:

  • 标准函数的倒置,可以在filter editor中手动应用这些,但是对于频繁使用,自定义函数更方便。
  • 表示复杂表达式的标准函数组合的函数。 例如,Within Days of X函数可能包含行,其中字段值在给定日期之前或之后的N天内,这省去了用户在两个不同日期之间配置标准函数的工作量。
  • 自定义DateTime函数,例如Is Weekend,N Days Ago等。

从v19.1开始,Filter Editor控件和Filter控件完全支持自定义函数,从而可以轻松实现上述方案和许多其他方案。

技术基础

自定义函数是实现接口ICustomFunctionDisplayAttributes的类,请注意如果需要服务器端对自定义函数的处理,则可以额外实现ICustomFunctionOperatorFormattable接口,但是在本文范围内,我们仅关注ICustomFunctionDisplayAttributes。

这些是接口实现所需的方法和属性:

  • Name - 您用来从代码中引用自定义函数的技术功能名称
  • DisplayName - GUI中显示的可读函数名称。 例如,名称为NotStartsWith的函数的DisplayName可能不是开头的
  • Image - Filter Control菜单中为该函数显示的图标,该属性类型为Object,但是下面的示例显示了如何使用现有的标准图像,也可以分配图像对象
  • Description - 用户在Filter Editor控件的文本面板中编辑表达式时,在弹出提示中的函数描述
  • Category - Expression Editor的函数类别,如果您打算仅将函数用于过滤器,则这无关紧要
  • MinOperatorCount, MaxOperatorCount, IsValidOperandCount - 该函数支持运算符数量,对于Filter和Filter Editor控件,所有三个值可以相等。 如果计划在Expression Editor中使用函数,则可以灵活地支持可变数量的运算符。
  • IsValidOperandType - 调用每个操作数来检查是否具有有效的类型,仅检查第一个操作数的筛选器
  • ResultType - 函数的返回值类型,Filter控件仅显示具有布尔结果类型的函数
  • Evaluate - 该函数在函数每次对一个数据字段求值时都会调用的方法,值在操作数数组中传递。 返回true包括相关行,返回false排除相关行。

最后,我们建议添加两个静态便利函数Register和Unregister。 这是一个可选步骤,但是实现很简单(请参见下文),并且它们以CriteriaOperator类型调用现有的帮助器。

示例

供您参考,下面是三个示例,它们涵盖了上面提到的三个最需要的方案。 第一个自定义函数称为NotBeginsWith,它是对标准函数BeginsWith的取反。

public class NotBeginsWithFunction : ICustomFunctionDisplayAttributes {
 public const string FunctionName = "NotBeginsWith";
 static readonly NotBeginsWithFunction instance = new NotBeginsWithFunction();
 public static void Register() {
 CriteriaOperator.RegisterCustomFunction(instance);
 }
 public static bool Unregister() {
 return CriteriaOperator.UnregisterCustomFunction(instance);
 }public string Name => FunctionName;
 public string DisplayName => "Does not begin with";
 public object Image => "FontSizeDecrease;Office2013";
 public string Description =>
 "Hides records when the field begins with the given value";
 public FunctionCategory Category => FunctionCategory.Text;public int MinOperandCount => 2;
 public int MaxOperandCount => 2;
 public bool IsValidOperandCount(int count) => count == 2;
 public bool IsValidOperandType(int operandIndex, int operandCount,
 Type type) => type == typeof(string);
 public Type ResultType(params Type[] operands) => typeof(bool);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;
 }
 }

这是第二个自定义函数InternalDaysOfToday,用于检查DateTime值是否在今天-N天和今天+ N天的时间范围内。

public class WithinDaysOfTodayFunction : ICustomFunctionDisplayAttributes {
 public const string FunctionName = "WithinDaysOfToday";
 static readonly WithinDaysOfTodayFunction instance =
 new WithinDaysOfTodayFunction();
 public static void Register() {
 CriteriaOperator.RegisterCustomFunction(instance);
 }
 public static bool Unregister() {
 return CriteriaOperator.UnregisterCustomFunction(instance);
 }public string Name => FunctionName;
 public string DisplayName => "Within days of today";
 public object Image => "SwitchTimeScalesTo;Size16x16;Colored";
 public string Description =>
 "Shows records when the field value within X days of today";
 public FunctionCategory Category => FunctionCategory.DateTime;public int MinOperandCount => 2;
 public int MaxOperandCount => 2;
 public bool IsValidOperandCount(int count) => count == 2;
 public bool IsValidOperandType(int operandIndex, int operandCount,
 Type type) => operandIndex == 0 && type == typeof(DateTime) ||
 operandIndex == 1 && type == typeof(int);
 public Type ResultType(params Type[] operands) => return typeof(bool);public object Evaluate(params object[] operands) {
 DateTime dt = Convert.ToDateTime(operands[0]);
 int days = Convert.ToInt32(operands[1]);
 DateTime start = DateTime.Today.AddDays(-days);
 DateTime end = DateTime.Today.AddDays(days);
 return dt >= start && dt <= end;
 }
 }

最后,IsWeekend测试DateTime值是星期六还是星期天。

public class IsWeekendFunction : ICustomFunctionDisplayAttributes {
 public const string FunctionName = "IsWeekend";
 static readonly IsWeekendFunction instance = new IsWeekendFunction();
 public static void Register() {
 CriteriaOperator.RegisterCustomFunction(instance);
 }
 public static bool Unregister() {
 return CriteriaOperator.UnregisterCustomFunction(instance);
 }public string Name => FunctionName;
 public string DisplayName => "Is weekend";
 public object Image => "DayView;Office2013";
 public string Description =>
 "Shows records when the field value is on Saturday or Sunday";
 public FunctionCategory Category => FunctionCategory.DateTime;public int MinOperandCount => 1;
 public int MaxOperandCount => 1;
 public bool IsValidOperandCount(int count) => count == 1;
 public bool IsValidOperandType(int operandIndex, int operandCount,
 Type type) => type == typeof(DateTime);
 public Type ResultType(params Type[] operands) => typeof(bool);public object Evaluate(params object[] operands) {
 DateTime dt = Convert.ToDateTime(operands[0]);
 return dt.DayOfWeek == DayOfWeek.Sunday ||
 dt.DayOfWeek == DayOfWeek.Saturday;
 }
 }