程序员的资源宝库

网站首页 > gitee 正文

支持ajax的名称选择器用户控件(ajax可以实现的功能)

sanyeah 2024-04-01 11:44:52 gitee 4 ℃ 0 评论

    • 下载source code - 33.6 KB

(You need to configure the web.config for this sample to work.)

介绍

我使用Facebook这类网站的次数越多,我就越着迷于它们使用的控件。我对一个控件非常着迷,那就是用来寻找其他用户的控件。在键入时,格式非常好的下拉列表将显示名称和其他信息。选择其中一个名称将触发更多服务器端工作。本文是我尝试使用Microsoft stack: SQL for database, ASP开发类似的体验。代码使用的是NET 2.0,用于sizzle的是AJAX控制工具包,还有一些JavaScript,因为没有其他方法可以绕过它。

背景

我以前尝试过这种控制方式,但对结果很不满意。我过去尝试的最大问题是它使用了回调技术,这种技术严重依赖于JavaScript并通过网络发送文本。这种方法有很多问题……虽然公认的更瘦,但提供的控制比我喜欢的少。相反,我想要一种客户端用户体验的方法,但实际上我能够在服务器上操作,并交付有意义的、格式良好的结果。我能想到的实现这一点的唯一方法是使用异步回发。然而,使用回发一个主要缺点是控制往往失去焦点后回发(中或不是!),这是一个灾难在一个文本框,用户希望能够自由类型,而不必重新选择,那么我需要克服。

此控件的另一个目标是在我的应用程序中完全可移植。这个控件被设计成可以放置在我的应用程序的任何页面的任何位置,而不需要任何代码配置。

关键点1 -数据库的东西

当然,像这样的搜索可以在很多情况下工作。本文附带的示例主要关注人。在SQL Server 2005中,我有三个表。

  • 具有姓、名、JobTitleID和LocationID的Person表。
  • 带有JobTitleID和JobTitle的JobTitle表。
  • 一个带有LocationID和Location的位置表。

然后使用以下存储过程搜索匹配的人。这个存储过程的重要部分是WHERE子句。我正在匹配用户输入的字符串的名字,或姓,或首<空格>最后,或最后逗号空格第一。

CREATE PROCEDURE usp_searchPeople (@namecontains nvarchar(20))
AS

    SELECT
        P.FirstName,
        P.LastName,
        J.JobTitle,
        L.Location
    FROM
        Person as P 
        INNER JOIN JobTitle as J on P.JobTitleID = J.JobTitleID
        INNER JOIN Location as L on P.LocationID = L.LocationID
    WHERE
        @namecontains <> ''
        AND
        P.LastName like N'%' + @namecontains + '%'
        OR P.Firstname like N'%' + @namecontains + '%'
        or ((P.LastName + ', ' + P.FirstName) like N'%' + @namecontains + '%')
        OR ((P.FirstName + ' ' + P.LastName) like N'%' + @namecontains + '%')
    ORDER BY 
        P.Lastname, P.FirstName

关键#2 -课程

我非常喜欢在我的设计器页面上使用ObjectDataSources。使用它们某种程度上迫使您将数据库交互抽象为类——这在OO的世界中是一件好事。因此,这里是我的两个People类:一个只是Person类,而People类处理获取数据和返回数据集(如果我们想要ObjectDataSource检索并最终填充GridView,这是必需的)。这些课程没有什么突破性的。但是,在People类中,我使用了这个应用程序。从SQL Server中获取数据。如果你不熟悉……你真的应该这么做。真的很好。另外,我已经在我的Web中设置了数据库连接。配置(如果你要测试这个,你必须调整)。

Public Class Person

    Private _Firstname As String

    Public Property Firstname() As String
        Get
            Return _Firstname
        End Get
        Set(ByVal value As String)
            _Firstname = value
        End Set
    End Property


    Private _Lastname As String

    Public Property Lastname() As String
        Get
            Return _Lastname
        End Get
        Set(ByVal value As String)
            _Lastname = value
        End Set
    End Property


    Private _JobTitle As String

    Public Property JobTitle() As String
        Get
            Return _JobTitle
        End Get
        Set(ByVal value As String)
            _JobTitle = value
        End Set
    End Property


    Private _Location As String

    Public Property Location() As String
        Get
            Return _Location
        End Get
        Set(ByVal value As String)
            _Location = value
        End Set
    End Property


End Class

Imports Microsoft.ApplicationBlocks.Data
Public Class People

    Public Function SearchPeople(ByVal searchstr As String) As DataSet
        Dim strSQL As String = "usp_SearchPeople"
        Dim params(0) As SqlClient.SqlParameter
        params(0) = New SqlClient.SqlParameter("@namecontains", searchstr)


        Try
            PeopleDS = SqlHelper.ExecuteDataset(_
              ConfigurationManager.AppSettings("MyDataBase"), _
              CommandType.StoredProcedure, strSQL, params)
        Catch ex As Exception
            _ErrMsg = ex.Message
        End Try

        Return PeopleDS

    End Function

    Private _ErrMsg As String

    Public Property ErrMsg() As String
        Get
            Return _ErrMsg
        End Get
        Set(ByVal value As String)
            _ErrMsg = value
        End Set
    End Property


    Private _PeopleDS As DataSet = New DataSet()
    Public Property PeopleDS() As DataSet
        Get
            Return _PeopleDS
        End Get
        Set(ByVal value As DataSet)
            _PeopleDS = value
        End Set
    End Property


    Private _Person As Person

    Public Property Person() As Person
        Get
            Return _Person
        End Get
        Set(ByVal value As Person)
            _Person = value
        End Set
    End Property

End Class

关键点#3 -用户控制设计器页面

现在我们有足够的资源来创建用户控件设计器页面。在我们完成之前,页面上会有很多内容,但这里是重要的部分:

    • 搜索框——用户在其中键入一些JavaScript引用。我将在第4节中讨论这些函数。
& lt; asp:文本框runat = " server " ID =“txtSearchStr”

宽度= " 260 "风格= "边境:没有”;

得到焦点= " startSearching (this.id)“onblur =“endSearching ()/比;
    • 更新面板——我将控件的其余部分封装在一个UpdatePanel中,这样当用户在搜索框中键入时,网格就可以更新而不需要整个页面的回发。注意,我已经向面板添加了一个触发器。这最终将从JavaScript触发,但这对于此工作的顺利进行至关重要。
asp:UpdatePanel runat="server" ID="upPersonSearch" UpdateMode="conditional">
& lt; Triggers>
控件="txtSearchStr" EventName="TextChanged" />
& lt; / Triggers>
& lt; ContentTemplate>
    • ObjectDataSource——这是控件的工作机器,负责检索数据并将其传递给GridView。这里唯一有趣的地方是将搜索参数的默认值设置为一些随机字符,这样当页面首次加载时,GridView请求它的数据,什么也不会传递。还要注意,文本框控件被标识为SelectParameter。
& lt; asp: ObjectDataSource来runat = " server " ID =“odsPeopleList”

TypeName = " ASPNETPersonSearch。人“SelectMethod =“SearchPeople”比;
& lt; SelectParameters>
& lt; asp: ControlParameter ControlID = " txtSearchStr "

Name = " namecontains“DefaultValue = " @@ # # $ $”/比;
& lt; / SelectParameters>
& lt; / asp: ObjectDataSource>
    • 在我的例子中,结果显示在GridView中。当然,您可以使用ASP中的任何数据对象。NET (ListView, DataView等)。GridView连接到ObjectDataSource并将PeopleID标识为DataKey。当我们处理用户选择搜索结果时,这一点非常重要。
& lt; asp:显示数据表格runat = " server " ID =“gvPeopleList”

DataSourceID = " odsPeopleList " AutoGenerateColumns = " false "

ShowFooter = " false " ShowHeader = " false "

DataKeyNames =“PeopleID”比;
& lt; Columns>
& lt; asp: TemplateField>
& lt; ItemTemplate>
& lt; div>
& lt; asp: LinkButton runat = " server "

风格= "文字修饰:没有”;ID = " btn1 "

SkinID = "普通" CommandName = "选择" /比;
& lt; / div>
& lt; / ItemTemplate>
& lt; / asp: TemplateField>
& lt; / Columns>
& lt; EmptyDataTemplate>
asp:Label runat="server" ID="lbl1" />
& lt; / EmptyDataTemplate>
& lt; / asp: GridView>

让我们停下来看看我们有什么。在页面上,我们有一个文本框、一个UpdatePanel、一个ObjectDataSource和一个网格。我们可以这样运行页面,它会工作…排序的。我需要克服的大问题是文本框的TextChanged属性将只有当用户单击enter>或者& lt; tab>关键。但是,那不是我想要的。我想让它在他们打字时开火。叹息…JavaScript。

关键#4 - JavaScript

要实现即时输入和查看结果的感觉,您需要某种JavaScript。我的第一个想法是使用OnKeyUp或OnKeyPress JavaScript函数…事实上,我一开始就是这么做的。但有一个问题。如果person以任何类型的速度键入,并且您触发了数据的post-back和update,那么在person键入的内容和提交的搜索字符串之后就会有延迟。你可以把所有的JavaScript封装在一个计时器函数中,它可以跟踪按键之间的时间,如果用户键入更多,就会重置时钟……但我对这种方法进行的测试越多,它的效果就越糟糕。这时我想到了以下几点:

  • 在文本控件的焦点上启动一些功能,在模糊上停止它。
  • 然后,不再响应用户的击键,而是每隔一秒左右触发回发。

当然,这似乎有些过分,所以我设置了一个变量来保存最后使用的搜索值。如果它与搜索框中已经存在的内容相匹配(也就是说,person已经键入了一个值,搜索已经触发,并且他们在那里停留了20秒查看名称),JavaScript就会跳过回发,节省了我们到服务器的往返时间。

下面是来自JavaScript的亮点:

    • 全局变量——在函数中用作引用。实际上,我没有将prm变量放到PeopleSearch.js项目文件中。相反,我将它放置在用户控件设计器页面上。我还把它包装在逻辑中,检查它是否已经存在——创建它或者是为了其他目的,或者是因为我在同一页面上放置了两个这样的用户控件。这是因为我需要在渲染页面的DOM中引用一些东西,而不是创建一个新对象。
//在peoplesear .vb文件中定义的全局变量
var intervalTimerId = 0;
var currenttext = "";

// PeopleSearch顶部的脚本引用。ascx页面
& lt;脚本语言= " javascript " type = " text / javascript祝辞
//如果同一页面上有两个用户控件,
//我们只设置该变量一次
if(!prm) {var prm = Sys.WebForms.PageRequestManager.getInstance();}
& lt; / script>
    • 开始搜索-让派对开始。这真的没什么了不起的。简单地告诉JavaScript每秒钟开始执行doPostBackAsynch函数。这个函数由文本框的OnFocus事件触发。当在文本框中引用时,请注意,我以编程方式设置了控件的ID(使用this.id)。这对于能够在任何地方重用此控件非常重要——而且可以在同一页面上多次重用。
函数startSearching (cid) {
intervalTimerId = setInterval ("doPostBackAsync('" + cid + "', ")", 1000);
}
    • 结束搜索-够了!杀死间隔。
函数endSearching () {
clearInterval (intervalTimerId);
}
    • doPostBackAsync -啊,真神奇。首先,从文本框中收集搜索字符串。然后,确保用户输入了长度合理的搜索字符串(大于2),并且文本框中的搜索字符串与全局变量currenttext不匹配。这是我用来确保我们不会在有人把车停在文本框里然后去买咖啡时碰到无数次服务器呼叫的技巧。当然,JavaScript的间隔会持续发射,但是,射击,那是在客户端…所以,谁在乎呢:)

在这个函数中真正好的东西是下面的代码:

prm._asyncPostBackControlClientIDs.push (eventName);

我在谷歌上搜索了一下,发现了这个,真是天才。下面介绍如何从JavaScript触发UpdatePanel。push方法模拟剩余dopostback事件。这里是完整的代码:

函数doPostBackAsync(eventName, eventArgs)
{var tbox = document.getElementById(eventName);
如果(tbox.value。长度比;2,,tbox。value != currenttext)
{
如果(!数组。包含(人口、难民和移民事务局。_asyncPostBackControlIDs, eventName))
{prm._asyncPostBackControlIDs.push (eventName);}

如果(!数组。包含(人口、难民和移民事务局。_asyncPostBackControlClientIDs, eventName))
{
prm._asyncPostBackControlClientIDs.push (eventName);
}
剩余dopostback (eventName, eventArgs);
currenttext = tbox.value;
}

}

好了,差不多完成了。最后一部分是注册所有这些JavaScript。我选择在控件的设计器页面上使用ScriptManagerProxy。这允许您在需要的地方引用和注册.js文件的函数,而不是将引用放在使用.aspx的页面上,或者更糟的是放在母版页面上。

<asp:ScriptManagerProxyrunat="server"ID="sm1">
    <Scripts>
        <asp:ScriptReferencePath="~/js/EmployeeSearch.js"/>
    </Scripts>
</asp:ScriptManagerProxy>

关键点5 -代码背后

现在我们已经完成了所有这些工作,允许我们在服务器端控制事情……让我们看看我们能做什么。首先,由于这是一个用户控件,因此可以合理地假设您希望绑定数据并将数据传递回使用页面。所以,你必须导入网页。UI和ComponentModel名称空间。

Imports System.Web.UI
Imports System.ComponentModel

接下来,让我们考虑一下在GridView中得到的结果会是什么样子。我可以用姓和名来表示,但那样太无聊了。相反,当行被绑定时,我选择获取搜索字符串,检查它是否有逗号(person正在输入Lastname、Firstname),然后高亮显示结果,以显示在搜索中匹配的结果。这看起来工作量很大,但它让用户体验非常自然。再次检查文章顶部的循环图像。注意,当我输入我的名字,然后是最后一个名字时,结果会在我输入时出现。然后,当我走到最后一刻首先,结果切换到我输入搜索的方式。此外,我在这个方法中使用了People对象,这使得处理名称非常简单。最后,我选择只展示由人们提供的结果。SearchPeople方法。你可以很容易地添加图片,链接,或其他关于这个人的东西在这种方法,以加强搜索结果。

Private Sub gvPeopleList_RowDataBound(ByVal sender As Object, _
            ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _
            Handles gvPeopleList.RowDataBound
    'retrieve the text entered by the user
    Dim searchStr As String = txtSearchStr.Text

    If e.Row.RowType = DataControlRowType.DataRow Then

        'create new person object
        Dim peep As New Person()

        'when we are done inspecting the input text, 
        'this will hold the string to show users
        'in the result box
        Dim Namestr As String = ""

        'if they person is entering Lastname, Firstname
        Dim strArr As String() = Split(searchStr, ",")

        'if there is a comma in the search text
        If UBound(strArr) > 0 Then  'there was a comma
            'if the Last name text is more than just white space, 
            'show the matches using the Highlight function
            peep.Lastname = IIf(Trim(strArr(0)).Length > 0, _
                 Utilities.Highlight(Trim(strArr(0)), _
                 e.Row.DataItem("LastName")), _
                 e.Row.DataItem("LastName"))

            'if the first name text is more than just white space, 
            'show the matches using the Highlight function
            peep.Firstname = IIf(Trim(strArr(1)).Length > 0, _
                 Utilities.Highlight(Trim(strArr(1)), _
                 e.Row.DataItem("Firstname")), _
                 e.Row.DataItem("Firstname"))

            'set the presentation variable
            Namestr = peep.Lastname & ", " & peep.Firstname
        Else
            'if there was no comma, then search for a space
            strArr = Split(searchStr)
            If UBound(strArr) > 0 Then
                'if there was a space....
                peep.Lastname = IIf(Trim(strArr(1)).Length > 0, _
                     Utilities.Highlight(Trim(strArr(1)), _
                     e.Row.DataItem("LastName")), _
                     e.Row.DataItem("LastName"))
                peep.Firstname = IIf(Trim(strArr(0)).Length > 0, _
                     Utilities.Highlight(Trim(strArr(0)), _
                     e.Row.DataItem("Firstname")), _
                     e.Row.DataItem("Firstname"))
                Namestr = peep.Firstname & " " & peep.Lastname
                'if all the other options fail, just highlight 
                'the First and Last names from the search
                'results and set the presentation variable
            Else
                peep.Firstname = Utilities.Highlight(searchStr, _
                                     e.Row.DataItem("Firstname"))
                peep.Lastname = Utilities.Highlight(searchStr, _
                                     e.Row.DataItem("LastName"))
                Namestr = peep.Lastname & ", " & peep.Firstname
            End If
        End If


        'set the persons location
        peep.Location = e.Row.DataItem("Location")

        'if the person has a job title, set it
        peep.JobTitle = IIf(e.Row.DataItem("JobTitle") _
           Is DBNull.Value, "", e.Row.DataItem("JobTitle"))


        'Find the link button in the grid
        Dim btn As LinkButton = TryCast(e.Row.FindControl("btn1"), LinkButton)

        'set the text of the link button
        btn.Text = "<b>" & Namestr & "</b>" & _
                   "<br />" & peep.JobTitle & _
                   " - " & peep.Location

    ElseIf e.Row.RowType = DataControlRowType.EmptyDataRow Then
        'If there person has entered more than two characters, 
        'show them that there were no search results
        If txtSearchStr.Text.Length > 2 Then
            Dim lbl As Label = TryCast(e.Row.FindControl("lbl1"), Label)
            lbl.Text = "No matching records were found for the search string: <i>" & _
                       searchStr & "</i>."

            'otherwise, do not show the empty row template
        Else
            e.Row.Visible = False
        End If

    End If
End Sub

好的,结果看起来很漂亮。现在,让我们考虑如何处理用户对结果的单击。首先,我喜欢设置一个可绑定属性,它总是从GridView的选定行返回DataKey PeopleID。这是将gvPeopleList GridView的选择值传递到消费页面的一种非常优雅的方式。理论上,您还可以传入一个值来表示PeopleID,但是我在这篇文章中没有介绍这种情况。

Private _PeopleID As Integer = 0
<Bindable(True)> <Browsable(True)> _
Public Property PeopleID() As Integer
    Get
        Return CType(gvPeopleList.DataKeys(_
          gvPeopleList.SelectedIndex).Item("PeopleID"), Integer)
    End Get
    Set(ByVal value As Integer)
        _PeopleID = value
    End Set
End Property

我们现在可以告诉消费页面选择了什么“值”,但这还不够好。我们还需要触发一个公共事件,消费页面可以观察和反应-就像我们的用户控件是一个“正常的”ASP控件,像一个下拉列表的selectedindexchange事件。在本例中,我已经将这个公共事件(PersonSelected)连接到GridView的selectedindexchangeevent。

Public Event PersonSelected(ByVal sender As Object, ByVal e As System.EventArgs)

Private Sub gvPeopleList_SelectedIndexChanged(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles gvPeopleList.SelectedIndexChanged
    RaiseEvent PersonSelected(sender, e)
End Sub

唷。就是这样。这就是控制。您可以在这段代码后面疯狂地做各种各样奇妙的事情。事实上,对于这种控制,我有几个想法。但是,正如所宣传的那样,我们现在有了一个用户控件,可以在我的解决方案中放到任何页面上,以搜索人员并返回匹配的ID。相反,我更喜欢将控件添加到我的web中。配置,就像这样:

<system.web>
    <pages>
      <controls>
         <addtagPrefix="uc1"src="~/UserControls/PersonSearch.ascx"tagName="PersonSearch"/>
      </controls>
    </pages>
</system.web>

关键点#6 -使用控件

如果你看一下我附加到这篇文章的解决方案,你会看到有很多文件进入这个控件。

  • 类/人。vb、类/ Person.vb
  • css / default .
  • js / PersonSearch.js
  • 用户控件/ PeopleSearch。ascx用户控件/ PeopleSearch.ascx.vb

这似乎是一大笔开销,但实际上并非如此。最棒的是,现在我们可以在一个“普通”的ASPX页面中毫无麻烦地使用这个相当复杂的项目集合。下面是一个设计示例和代码隐藏页面,以“使用”这个控件:

<%@PageLanguage="vb"AutoEventWireup="false"CodeBehind="Default.aspx.vb"Inherits="ASPNETPersonSearch._Default"%>

<!DOCTYPEhtmlPUBLIC"-//W3C//DTDXHTML1.0Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<htmlxmlns="http://www.w3.org/1999/xhtml">
<headrunat="server">
    <title>Untitled Page</title>
    <linkhref="css/default.css"rel="stylesheet"type="text/css"/>
</head>
<body>
    <formid="form1"runat="server">
        <asp:ScriptManagerID="ScriptManager1"runat="server"/>
    <div>
        <table>
            <tr>
                <td>Choose a Person</td>
                <td><uc1:PersonSearchrunat="server"id="ps1"/></td>
            </tr>
        </table>
    </div>
    </form>
</body>
</html>

非常干净…对吧?和后台代码:

Partial Public Class _Default
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, _
                  ByVal e As System.EventArgs) Handles Me.Load

    End Sub

    Private Sub ps1_PersonSelected(ByVal sender As Object, _
            ByVal e As System.EventArgs) Handles ps1.PersonSelected
        Dim pid As Integer = ps1.PeopleID
        'do stuff with the selected person ID
    End Sub
End Class

你甚至可以有一个UpdatePanel并使用PersonSelected事件作为触发器,如下所示:

<table>
    <tr>
        <td>Choose a Person</td>
        <td>
            <uc1:PersonSearchrunat="server"id="ps1"/>
        </td>
    </tr>
</table>
<asp:UpdatePanelrunat="server"ID="upResult"UpdateMode="conditional">
    <Triggers>
        <asp:AsyncPostBackTriggerControlID="ps1"EventName="PersonSelected"/>
    </Triggers>
    <ContentTemplate>
        <asp:Labelrunat="server"ID="lblresult"/>
    </ContentTemplate>
</asp:UpdatePanel>

您应该知道,如果您只是复制附加到此文章的解决方案并尝试运行它,它将不会工作。必须在web中更改数据库引用。配置文件。如果你只是尝试运行这个项目,你可能会得到这个错误:

The DataSet in data source 'odsPeopleList' does not contain any tables.

这个错误意味着SQL在你的人。SearchPeople方法不是填充数据集(而不是返回不匹配的结果)。只有在查询本身出现问题时才会这样做。

结论

就是这样了。一个真正可移植的,漂亮的,易于实现的搜索组合框使用AJAX, ASP。NET和一点JavaScript。我在使用这个控件时发现的一件事是,如果您将控件本身放在一个UpdatePanel中,您的用户将失去对控件的关注,除非您将“父”UpdatePanel配置为:ChildrenAsTriggers="false"。但是,您可以在任何。aspx页面或在任何子页,甚至母版页,如果你愿意的话。

我在这篇文章中没有提到的一件事是在网格中样式化结果。我在示例项目中包含了一个.css文件,但它根本不处理网格。

本文转载于:http://www.diyabc.com/frontweb/news14816.html

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表