独上高楼网站
  •    你所在位置:首页 VS.netASP.net控件〉[ASP.NET 控件实作] 解决 DropDownList 成员 Value 值相同产生的问题
  • [ASP.NET 控件实作] 解决 DropDownList 成员 Value 值相同产生的问题
  • 作者:jeff377  文章来源:博客园  发布日期:2008-10-31  浏览次数:60
  • 打印这篇文章
  • DropDownList 控制页的成员清单中,若有 ListItem 的 Value 值是相同的情形时,会造成 DropDownList 无法取得正确的 SelectedIndex 属性值、且无法正确引发 SelectedIndexChanged 事件的问题;今天刚好在网络上看到有人在询问此问题,所以本文将说明这个问题的源由,并修改 DropDownList 控件来解决这个问题。

    程序代码下载:ASP.NET Server Control - Day29.rar

    一、DropDownList 的成员 Value 值相同产生的问题

    我们先写个测试程序来描述问题,在页面上放置一个 DropDownList 控件,设定 AutoPostBack=True,并加入四个 ListItem,其中 "王五" 及 "陈六" 二个 ListItem 的 Value 值相同。

    asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True">
    asp:ListItem Value="0">張三〈/asp:ListItem>
    asp:ListItem Value="1">李四〈/asp:ListItem>
    asp:ListItem Value="2">王五〈/asp:ListItem>
    asp:ListItem Value="2">陳六〈/asp:ListItem>
    /asp:DropDownList>

     

     

    在 DropDownList 的 SelectedIndexChanged 事件,输出 DropDownList 的 SelectedIndex 及 SelectedValue 属性值。

        Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged
            Dim sText As String
     
            sText = String.Format("DropDownList: Index={0} Value={1}", DropDownList1.SelectedIndex, DropDownList1.SelectedValue)
            Me.Response.Write(sText)
        End Sub

     

     

     

    执行程序,在 DropDownList 选取 "李四" 这个选项时,会正常显示该成员的 SelectedIndex 及 SelectedValue 属性值。

    image 

    接下来选取 "陈六" 这个选项时,竟然发生奇怪的现象,DorpDownList 竟然显示相同 Value 值的 "王五" 这个成员的 SelectedIndex 及 SelectedValue 属性值。

    image image

     

    二、问题发生的原因

    我们先看一下 DropDownList 输出到客户端的 HTML 原始码。

    select name="DropDownList1" onchange="javascript:setTimeout('__doPostBack(\'DropDownList1\',\'\')', 0)" id="DropDownList1">
    option selected="selected" value="0">張三〈/option>
    option value="1">李四〈/option>
    option value="2">王五〈/option>
    option value="2">陳六〈/option>
    /select>

     

     

    DropDownList 是呼叫 __doPostBack 函式,只传入 eventTarget参数 (对应到 __EVENTTARGET 这个 HiddenField) 为 DropDownList 的 ClientID;当 PostBack 回伺服端时,在 DropDownList 的 LoadPostData 方法中,会取得客户端选取的 SelectedValue 值,并去寻找对应的成员的 SelectedIndex 值。可是问题来了,因为 "王五" 与 "陈六" 的 Value 是相同的值,当在寻找符合 Value 值的成员时,前面的选项 "王五" 会先符合条件而传回该 Index 值,所以先造成取得错误的 SelectedIndex 。

    Protected Overridable Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As NameValueCollection) As Boolean
        Dim values As String() = postCollection.GetValues(postDataKey)
        Me.EnsureDataBound
        If (Not values Is Nothing) Then
            MyBase.ValidateEvent(postDataKey, values(0))
            Dim selectedIndex As Integer = Me.Items.FindByValueInternal(values(0), False)
            If (Me.SelectedIndex 〈> selectedIndex) Then
                MyBase.SetPostDataSelection(selectedIndex)
                Return True
            End If
        End If
        Return False
    End Function

     

     

     

    三、修改 DropDownList 控件来解决问题

    要解决这个问题最好的方式就是直接修改 DropDownList 控件,自行处理前端呼叫 __doPostBack 的动作,将客户端 DropDownList 选择 SelectedIndex 一并传回伺服端。所以我们继承 DropDownList 命名为 TBDropDownList,覆写 AddAttributesToRender 来自行输出 PostBack 的客户端指令码,我们会用一个变量记录 AutoPostBack 属性,并强制将 AutoPostBack 属性值设为 False,这是为了不要 MyBase 产生 PostBack  的指令码;然后再自行输出 AutoPostBack 客户端指令码,其中 __doPostBack 的 eventArgument 参数 (对应到 __EVENTARGUMENT 这个 HiddenField) 传入 this.selectedIndex。

            Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)
                Dim bAutoPostBack As Boolean
                Dim sScript As String
     
                '記錄 AutoPostBack 值,並將 AutoPostBack 設為 False,不要讓 MyBase 產生 PostBack 的指令碼
                bAutoPostBack = Me.AutoPostBack
                Me.AutoPostBack = False
     
                MyBase.AddAttributesToRender(writer)
     
                If bAutoPostBack Then
                    MyBase.Attributes.Remove("onchange")
                    sScript = String.Format("__doPostBack('{0}',{1})", Me.ClientID, "this.selectedIndex")
                    writer.AddAttribute(HtmlTextWriterAttribute.Onchange, sScript)
                    Me.AutoPostBack = True
                End If
            End Sub

     

     

    在页面上放置一个 TBDropDownList 控件,设定与上述案例相同的成员清单。

    bee:TBDropDownList ID="DropDownList2" runat="server" AutoPostBack="True">
    asp:ListItem Value="0">張三〈/asp:ListItem>
    asp:ListItem Value="1">李四〈/asp:ListItem>
    asp:ListItem Value="2">王五〈/asp:ListItem>
    asp:ListItem Value="2">陳六〈/asp:ListItem>
    /bee:TBDropDownList>

     

    执行程序查看 TBDropDownList 控件的 HTML 原始码,呼叫 __doPostBack 函式的参数已经被修改,eventArgument 参数会传入该控件的 selectedIndex。

    select name="DropDownList2" id="DropDownList2" onchange="__doPostBack('DropDownList2',this.selectedIndex)">
    option selected="selected" value="0">張三〈/option>
    option value="1">李四〈/option>
    option value="2">王五〈/option>
    option value="2">陳六〈/option>
    /select>

     

    接下来还要覆写 LoadPostData 方法,取得 __EVENTARGUMENT 这个 HiddenField 的值,并判断与原 SelectedIndex 属性值是否不同,不同的话传回 True,使其产生 SelectedIndexChanged 事件。

            Protected Overrides Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As NameValueCollection) As Boolean
                Dim values As String()
                Dim iSelectedIndex As Integer
     
                Me.EnsureDataBound()
                values = postCollection.GetValues(postDataKey)
     
                If (Not values Is Nothing) Then
                    iSelectedIndex = CInt(Me.Page.Request.Form("__EVENTARGUMENT"))
                    If (Me.SelectedIndex 〈> iSelectedIndex) Then
                        MyBase.SetPostDataSelection(iSelectedIndex)
                        Return True
                    End If
                End If
                Return False
            End Function

     

     

     

    四、测试程序

    在 TBDropDownList 的 SelectedIndexChanged 事件撰写如下测试程序代码。

        Protected Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList2.SelectedIndexChanged
            Dim sText As String
     
            sText = String.Format("TBDropDownList: Index={0} Value={1}", DropDownList2.SelectedIndex, DropDownList2.SelectedValue)
            Me.Response.Write(sText)
        End Sub

     

     

     

     

    执行程序,在 TBDropDownList 选取 "王五" 这个选项时,会正常显示该成员的 SelectedIndex 及 SelectedValue 属性值。

    image

    接下选取 Value 值相同的 "陈六" 这个选项,也会正常引发 SelectedIndexChanged ,并显示该成员的 SelectedIndex 及 SelectedValue 属性值。

    image 

  • 打印这篇文章
  • 与本文主题相关的文章
  • 返回首页