[ASP.NET 控件实作 Day29] 解决 DropDownList 成员 Value 值相同产生的问题

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 属性值。

[ASP.NET 控件实作 Day29] 解决 DropDownList 成员 Value 值相同产生的问题_第1张图片 

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

[ASP.NET 控件实作 Day29] 解决 DropDownList 成员 Value 值相同产生的问题_第2张图片 [ASP.NET 控件实作 Day29] 解决 DropDownList 成员 Value 值相同产生的问题_第3张图片

 

二、问题发生的原因

我们先看一下 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 属性值。

[ASP.NET 控件实作 Day29] 解决 DropDownList 成员 Value 值相同产生的问题_第4张图片

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

[ASP.NET 控件实作 Day29] 解决 DropDownList 成员 Value 值相同产生的问题_第5张图片

 

备注:本文同步发布于「第一届iT邦帮忙铁人赛」,如果你觉得这篇文章对您有帮助,记得连上去推鉴此文增加人气 ^^
http://ithelp.ithome.com.tw/question/10013457
http://ithelp.ithome.com.tw/question/10013458

你可能感兴趣的:([ASP.NET 控件实作 Day29] 解决 DropDownList 成员 Value 值相同产生的问题)