65.9K
CodeProject 正在变化。 阅读更多。
Home

如何构建强大的搜索表单

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (28投票s)

2013 年 3 月 30 日

GPL3

10分钟阅读

viewsIcon

113383

downloadIcon

3127

用于构建高级搜索表单的设计模式和实现示例

简介

每个企业应用程序通常都有一个或几个主要的搜索表单,供多种类型的用户使用。这些表单通常用于支持对应用程序中关键对象(如订单、客户、交易、事件等)的高级搜索,使用多种不同的条件。最常见的是,用于输入这些条件的用户界面由一组控件组成,例如文本框、下拉列表或多选列表框,以指定一个或多个条件值。虽然这种方法对于简单的搜索表单运行良好,但在灵活性和用户友好性方面,对于更高级的搜索表单可能会力不从心,我们将在下一节讨论这一点。

在本文中,我们将介绍一种更强大、更灵活的方法,帮助您设计高级搜索表单。我们还将展示一个实现示例,该示例允许您几乎无需编写代码即可使用此方法,这可以帮助您轻松地将任何搜索表单转变为高级且强大的搜索表单。

传统方法的问题

现在,让我们讨论一下使用常规控件输入搜索条件的传统方法存在哪些问题。

PowerSearch/SearchCriteria_Traditional.png

空值。 通常,搜索条件控件中没有值意味着我们不想按此字段进行搜索。根据控件的不同,它可能是一个空的文本框,列表框中没有选择,或者是下拉列表中的一个特殊空白项,也可能显示“任意 <字段标签>”或类似内容。但是,有时我们可能特别想搜索在某个字段中没有值的对象。熟悉 SQL 的人会将其识别为 IS NULL 子句。在这种情况下,我们必须发挥创意,并根据我们使用的控件以不同的方式处理它。例如,对于文本字段,我们可以让用户输入一个特殊值,例如 EMPTYBLANK,我们将在代码中特别处理它。对于下拉列表或列表框,我们必须在列表中添加一个带有类似文本的额外项,这又将在代码中以不同的方式处理。理想情况下,我们希望保持一致,对所有此类条件使用相同的特殊值,同时确保相应字段中永远不会出现与这些特殊值重合的实际值。

反向条件。 这是另一个相当常见的情况,我们希望从搜索中排除那些在某个字段中具有等于我们在该字段的搜索条件中指定的值(或其中一个值)的对象。用 SQL 术语来说,这被称为 NOT 运算符。如果我们的搜索表单既要支持包含又要支持排除此类对象,那么我们别无他法,只能为每个此类字段添加一个标志,以指示我们是要包含还是排除这些值。

其他标准运算符。 对于每种数据类型,您有时需要支持许多其他标准运算符。例如,对于文本字段条件,您可能需要区分值应与指定条件完全相等的情况和值应包含条件作为子字符串的情况。一些开发人员让用户在条件中输入 * 并检查 * 的存在以区分这些情况。另一个例子是当您想允许按数字范围或时间段进行搜索时。常见的方法是为“From”和“To”条件添加两个控件,并让用户填充其中一个或两个。

非标准运算符。 您可能会提供其他非标准运算符供搜索,这些运算符可能不需要任何额外值。例如,如果您允许按日期搜索,您可能希望有一个特殊值“最近 30 天”来搜索最近的事件或交易。您可以将其添加为特殊条件,但您要确保用户在选择该条件时无法输入任何自定义日期。

搜索条件的设计模式

为了以简洁一致的方式解决上述所有问题,您可能需要考虑以下设计模式。

PowerSearch/SearchCriteria_TextOperators.png每个搜索条件将由一个运算符组成,该运算符具有基于条件类型的预定义值列表,以及根据需要为选定运算符提供值的一个或两个输入字段。选择一个运算符将要么隐藏所有值字段(如果该运算符不需要值,例如“为空”或“最近 30 天”),要么在运算符旁边显示一个或两个值字段,例如一个值的“等于”或两个值的“介于”。

每个运算符也可以留空,并隐藏所有值字段,以表示此字段不应被过滤。任何运算符也可以具有反向运算符,例如“为空”和“不为空”。对于单值条件(例如“等于”)和多值条件(“是其中之一”),运算符可能不同。

正如你所看到的,一个单独的运算符“为空”将允许一致且明确地指定我们希望搜索给定字段没有值的对象。此外,您可以指定任意数量的反向运算符或任何其他标准或非标准运算符。这将使您能够为您的应用程序设计强大而高级的搜索表单。请注意,您不必在表单上的每个搜索字段都使用运算符。没有运算符的搜索字段将始终可见,并且在字段被填充时只是暗示一个运算符(通常是“等于”或“是其中之一”)。

PowerSearch/SearchCriteria_Operators.png

尽管它看起来很棒,但您可能想知道在每个表单中实现这样的设计需要多少编码。好吧,我们有好消息告诉您。请继续阅读,了解 Xomega Framework,它实现了大部分基础设施,以支持任何搜索表单的这种设计。

实现示例

为了帮助您更好地理解我们这种设计模式的实现示例,您可能需要快速了解实现它的框架的背景知识。

Xomega Framework 是一个强大的开源 .NET 框架,用于构建多层 ASP.NET、WPF 或 Silverlight 应用程序。Xomega Framework 中一个关键的表示层概念是数据对象,它根据众所周知的 MVC 设计模式充当表单的数据模型。数据对象由不同类型的数据属性组成,可以轻松地以声明方式绑定到 XAML 或 ASPX 中的 UI 控件。Xomega 数据属性最值得注意的是,除了单个值之外,它们还可以支持多个值和一些关于属性的元数据,例如可见性或可编辑性,这些都会自动反映在绑定控件的状态中。

那么,让我们看看所有这些如何帮助实现我们的搜索条件设计模式。为了支持这种设计,框架定义了一个 OperatorProperty,它可以与一个或两个其他数据属性关联,这些属性将保存条件的实际值。

OperatorProperty 继承自 EnumProperty,这使得它可以使用预定义运算符的缓存列表。列表中的每个运算符都可以配置不同的附加属性,例如它需要多少个值,它是为多值还是单值条件定义的,它作用于什么类型的属性(例如文本、数字或日期/时间)等。

OperatorProperty 使用每个运算符的这些附加属性,只显示适用于当前搜索字段的运算符,并酌情隐藏或显示相应的值字段。

如何使用实现示例

首先,您需要从 CodePlex 下载最新版本的 Xomega Framework,并在您的 Visual Studio 项目中添加对 Xomega.Framework.dll 的引用。

接下来,您需要在应用程序启动期间在 App.xaml.cs 中将运算符列表加载到 Xomega 查找缓存中。您可以硬编码它,但最简单的方法是嵌入随附的 operators.xml 文件并在启动期间将其加载到查找缓存中。

private void Application_Startup(object sender, StartupEventArgs e)
{
    Assembly asm = GetType().Assembly;
    LookupCache.Get(LookupCache.Global).LoadFromXml(
        asm.GetManifestResourceStream(
            asm.GetName().Name + ".operators.xml"), false);
}

定义您的条件对象非常简单。您所做的只是扩展基类 DataObject,声明属性名称的常量,并用这些名称初始化数据属性。对于 OperatorProperty,您将枚举类型设置为“operators”,这是我们在 XML 中命名的。以下代码演示了一个条件对象,其中包含一个运算符和两个日期/时间属性,用于日期/时间范围的 FromTo 值。

public class MyCriteriaObj : DataObject
{
    public const string Criteria = "Criteria";
    public const string Criteria2 = "Criteria2";
    public const string CriteriaOperator = "CriteriaOperator";
    
    protected override void Initialize()
    {
        DateTimeProperty crit = new DateTimeProperty (this, Criteria);
        DateTimeProperty crit2 = new DateTimeProperty (this, Criteria2);
        OperatorProperty op = new OperatorProperty(this, CriteriaOperator);
        op.EnumType = "operators";
        // op.AdditionalPropertyName = Criteria;
        // op.AdditionalPropertyName2 = Criteria2;
        // op.FilterFunc = o => op.IsApplicable(o) && o.Id != "ISNOTNULL";
    }
}

您可以将 OperatorProperty 显式与相应的值属性关联(参见注释行),但框架也使用命名约定默认检索它。如果您不想支持某些运算符,您可以轻松添加自定义函数以将它们从可用运算符列表中过滤掉,如最后一行注释所示。

您需要做的最后一件事是将 XAML 中的搜索条件控件绑定到条件对象数据属性。为此,您所要做的就是将条件控件上的 xom:Property.Name 依赖项属性设置为指向您的数据属性名称。您可以在 WPF 中使用 static 常量进行编译时验证,或者在 Silverlight 中硬编码属性名称,Silverlight 不支持 XAML 中的 x:Static 构造。以下代码片段演示了此 Xomega 属性绑定。

<Grid Name="pnlCriteria"
    xmlns:xom="clr-namespace:Xomega.Framework;assembly=Xomega.Framework"
    xmlns:l="clr-namespace:MyNamespace;assembly=MyAssembly">
 <Label Grid.Row="0" Grid.Column="0" Name="lblCriteria">Criteria:</Label>
 <ComboBox Grid.Row="0" Grid.Column="1" Name="ctlCriteriaOperator"
           xom:Property.Label="{Binding ElementName=lblCriteria}"
           xom:Property.Name="CriteriaOperator"/>
 <TextBox Grid.Row="0" Grid.Column="2" Name="ctlCriteria"
          xom:Property.Name="{x:Static l:MyCriteriaObj.Criteria}"/>
 <TextBox Grid.Row="0" Grid.Column="3" Name="ctlCriteria2"
          xom:Property.Name="{x:Static l:MyCriteriaObj.Criteria2}"/>
</Grid>

如果您现在运行您的应用程序,您的组合框将显示正确的运算符列表,并且选择一个运算符将适当地显示或隐藏条件字段。条件控件中的值将自动传播到相应的数据属性,因此您根本无需在代码中访问这些控件。

相应的 ASP.NET 页面的 ASPX 标记将与以下代码片段非常相似。

<asp:Panel runat="server" ID="pnlCriteria">
 <asp:Label runat="server" ID="lblCriteria">Criteria:</Label>
 <asp:DropDownList runat="server" ID="ctlCriteriaOperator" 
    Property="CriteriaOperator" LabelID="lblCriteria"/>
 <asp:TextBox runat="server" ID="ctlCriteria" Property="<%# MyCriteriaObj.Criteria %>"/>
 <asp:TextBox runat="server" ID="ctlCriteria2" Property="<%# MyCriteriaObj.Criteria2 %>"/>
</asp:Panel>

如您所见,使用 Xomega Framework 设计和构建强大的搜索条件对象非常简单,所有枚举、数据对象以及 ASP.NET 或 XAML 控件都可以从您的域和服务层的基于 XML 的模型自动生成。此视频演示了如何通过几个简单的步骤生成带有高级条件的整个搜索表单。 

要了解更多信息,您可以从 CodePlex 下载并试用示例应用程序。

此外,要了解有关 Xomega Framework 的更多信息,请阅读我们的文章 将 MVC 提升到 .Net 中的下一个层次MVC 方式的级联选择

结论

在本文中,我们讨论了传统搜索表单设计方法的一些问题。您了解了一种设计模式,它允许以简洁一致的方式解决这些问题并构建强大的高级搜索表单。您还了解了针对 WPF 和 Silverlight 应用程序的此设计模式的实现示例,该示例需要最少的编码即可构建高级搜索表单。

我们很乐意听取您对此的任何评论或问题。

历史

  • 11/1/21:文章的第一版。 
  • 11/6/6:更新以提及 ASP.NET 支持并指向 CodePlex 上的最新版本。 
  • 12/12/2:更新了示例应用程序。  
© . All rights reserved.