构建更好的自动完成用户控件






4.14/5 (9投票s)
扩展自动完成 Ajax 扩展程序的实用性
引言
使用 Facebook 一段时间后,我对他们如何在几个页面上实现文本搜索功能很感兴趣。我真正感兴趣的功能是他们的“实时”查找,然后生成一个过滤项列表。一旦您选择一个项,就会发生异步回发,并且页面上的更新面板会发生变化。这是一种非常巧妙且轻量级的方式来实现一系列相当复杂的控件行为。此外,它非常直观(我非常关心这一点)。因此,我开始构建一个类似的控件,并开发出了一个非常好的用户控件。

背景
这最初是一个 * .aspx 页面上的单个控件,但我很快发现我希望它“可重用”,因此我开始将其通用化。当然,这比我预期的要困难得多。
关键部分 #1 - ListSearch.asmx
首先,您需要为您的网站创建一个 Web 服务,该服务将由 AutoComplete 扩展器调用。有很多关于如何做到这一点的文章。我选择创建一个 `ListSearch` Web 服务,它可以包含任意数量的 `WebMethods`。对于此示例,我只有一个,但我也留下了一个典型数据库调用的示例。
数据集填满后,您需要将文本/值对发送到 `AutoComplete` 对象。实现此目的的秘诀是创建一个 `ListOf` 变量并用您的值对填充它,如下所示
Dim items as New List(Of String)
For Each datar As DataRow In ds.Tables(0).Rows
items.Add(AjaxControlToolkit.AutoCompleteExtender.CreateAutoCompleteItem
(datar.Item("Color"), datar.Item("ID")))
Next
Return items.ToArray()
关键部分 #2 - DataTextSearch.ascx
创建 Web 服务后,您就可以构建用户控件了。在此过程中出现了大量问题。在 *.aspx 页面上放置一个带有 `AutoComplete` 扩展器的文本框非常容易。我遇到的最大问题是尝试创建一个可以在同一页面上多次出现的数据输入对象。你看,`AutoCompleteExtender` 严重依赖 JavaScript,当页面上有多个这样的控件时,您需要多个“版本”的脚本来唯一地引用 `UserControl` 中的控件。
所以,首先,在 ascx 方面,你需要几样东西
- 文本框 - 用于保存用户将键入的值
- UpdatePanel - 我将整个控件放在一个更新面板中,因此对该控件的更改不一定会影响 `UserControl` 的“外部”或消费页面
- `TextBoxWatermarkExtender` - 只是一点点修饰,让控件更友好
- 隐藏按钮 - 当从 `AutoCompleteExtender` 中选择一个值并且 `UserControl` 已配置为这样做时,我们将使用此按钮“欺骗”控件提交此表单。这将允许我们混合 JavaScript 和异步回发行为。
- 结果面板 - 保存来自扩展器的结果 - 最初是隐藏的,并根据 `AutoCompleteExtenders` 方法绘制
- 隐藏 ID 字段 - 用于保存所选文本的 ID
- `AutoCompleteExtender` - 此控件的“主力”,处理所有 Ajax 风格的显示/隐藏/点击等。
这些控件的配置和样式非常简单。所以,我将让您浏览我的示例以了解详细信息。
关键部分 #3 - DataTextSearch.ascx.vb
当控件第一次加载时,如果我们没有回发,有很多事情要做。以下是分解:
- 根据用户控件的公开属性 `BoxWidth` 设置对象的宽度
SBox.Width = CType(BoxWidth, Unit)) BoxFrame.Style("width") = BoxWidth.ToString()
- 选择将用于收集信息的 `WebMethod`。我将其保留为一种选择,因为您的 *ListSearch.asmx* 可以包含任意数量的方法,然后您可以告诉此 `UserControl` 您希望从哪个搜索返回值。现在,我创建了一个单独的 `Sub` 来处理此配置,因为我使用了一个 `Public enum` 来对 `UserControl` 需要“知道”的方法进行编号。这使得将控件添加到 * .aspx 页面变得很好,因为当您键入时 - intellisense 将为您提供可用 Web 方法的“选择”。
Select Case ServiceMethod Case ServiceMethods.Colors autoComplete1.ServiceMethod = "GetColorsByName" Case ServiceMethods.States autoComplete1.ServiceMethod = "GetStatesByName" End Select
这是 `Public Enum`
Public Enum ServiceMethods Colors = 1 States = 2 End Enum
- 根据 `UserControl` 配置打开或关闭高亮显示并配置 `WaterMarkText`
If HighlightResults Then autoComplete1.OnClientPopulated = "ClientPopulated" End If If WaterMarktext.Length > 0 Then textPrompt.WatermarkText = WaterMarktext Else textPrompt.Enabled = False End If
- 最棘手的部分 - 设置处理客户端事件所需的 JavaScript。这是必要的,因为我们正在构建一个用户控件,它可以在同一页面上多次使用。因此,我们获取对控件字段的 `ClientID` 的引用,并设置一个 JavaScript 函数(也根据 `UserControlID` 唯一命名)并注册它。
Dim script As String = "function ControlClickSubmit_" & autoComplete1.ClientID & _ "(source, e) " & _ " { " & _ " var node; " & _ " var value = e.get_value(); " & _ " if (value) node = e.get_item(); " & _ " else " & _ " { " & _ " value = e.get_item().parentNode._value; " & _ " node = e.get_item().parentNode; " & _ " } " & _ " var text = (node.innerText) ? node.innerText : _ (node.textContent) ? node.textContent : _ node.innerHtml; " & _ " source.get_element().value = text; " & _ " document.getElementById_ ('" & HiddenCID & "').value = _ " value; " & _ " var btn = document.getElementById_ ('" & subBtn.ClientID & "'); " & _ " var bx = document.getElementById_ ('" & SBox.ClientID & "'); " & _ " bx.value = text; " & _ " btn.click();}"
- 设置 `AutoCompleteExtender` 的 `OnClientItemSelected` 事件
autoComplete1.OnClientItemSelected = _ "ControlClickSubmit_" & autoComplete1.ClientID
- 在页面上注册您创建的脚本
Page.ClientScript.RegisterClientScriptBlock(Page.GetType(), _ "ControlClickSubmit_" & autoComplete1.ClientID, script, True)
- 创建一个名为 `ValueSelected` 的 `Public Event`。这可以提升到 *.aspx 级别并像任何其他“正常”事件一样使用
Public Event ValueSelected(ByVal sender As Object, ByVal e As System.EventArgs)
- 当我们将按钮放在页面上时,我们给了它两个非常重要的属性。一个确保按钮不提交(`UseSubmitBehavior="false"`),另一个将按钮的点击事件绑定到我们的 *.vb 代码子程序(`OnClick="SelectAction"`)。在上面注册的 JavaScript 中,您会注意到我们 ` .click()` 一个按钮 `var`。我们点击的按钮是这个隐藏按钮 - 这样做将导致异步回发,允许我们在服务器端操作,而无需回发整个消费页面(*.aspx)。`SelectAction` 子程序非常无聊。它只是将选定的值设置到隐藏的 ID 字段,将搜索框的文本设置为所选项目的文本,并且根据控件的配置触发 `ValueSelected` 事件。这是真正精彩的部分。如果您触发该 `ValueSelected` 事件,您就可以在您的消费 *.aspx 中编写代码来监听该事件,并且如果它发生,则执行其他操作。我在 `Default.aspx` 页面中有一个示例。
关键部分 #4 - JavaScript 包含
我能够抽象化 `Autocomplete` 的高亮显示行为。它位于一个单独的 *js/Autocompletescript.js* 文件中,需要包含在任何使用页面中。您**不要**将此脚本文件的引用放在用户控件中。如果您只在每页使用一次控件,它会起作用,但当然,我希望能够将此控件多次放在同一页面上。
关键部分 #5 - 在您的 Web.Config 中注册控件
我不喜欢将引用放在我的 * .aspx 页面的标题中,所以我通常像这样在我的 *web.config* 中注册该项
<system.web>
<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions,
Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add tagPrefix="uc1" src="~/UserControls/DataTextSearch.ascx"
tagName="DataTextSearch"/>
<add assembly="AjaxControlToolkit" namespace="AjaxControlToolkit"
tagPrefix="cc1"/>
</controls>
</pages>
关键部分 #6 - Default.aspx - 消费者
现在控件已构建并在我们的 *web.config* 中正确引用,我们可以将其添加到消费页面中。*default.aspx* 页面引用该控件,然后在选择列表项后执行“操作”。
<uc1:DataTextSearch runat="server"
ID="ColorSearchControl"
WaterMarktext="Type a color"
OnClickSubmit="true"
ServiceMethod="Colors"
HighlightResults="true"
ClearAfterSelect="true" />
关键部分 #7 - Default.aspx.vb
我将几个控件放在一个更新面板中,并将我的 `DataTextSearch` 控件的 `ValueSelected` 事件设置为 `AsynchPostBack` 触发器。因此,此代码在部分回发时触发并更新 `Panel`。请注意,我们可以在 `Sub` 上使用“`Handles`”语法,它将项目的选择视为任何其他可绑定控件操作。
Protected Sub ColorSearchControl_ValueSelected(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles ColorSearchControl.ValueSelected
ValueSelectedLbl.Text = "The ID of the color you chose is: " & _
ColorSearchControl.SelectedItemID.ToString()
TextSelectedLbl.Text = "The Text of the color you chose is: " & _
ColorSearchControl.SelectedItemText.ToString()
TextSelectedLbl.Style("color") = ColorSearchControl.SelectedItemText
End Sub
我对这个控件抱有很大的期望。它是一个非常强大和灵活的框架,可以为用户提供“自然”的搜索体验。
历史
- 2009 年 4 月 21 日:初始发布