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 属性值。
接下来选取 "陈六" 这个选项时,竟然发生奇怪的现象,DorpDownList 竟然显示相同 Value 值的 "王五" 这个成员的 SelectedIndex 及 SelectedValue 属性值。
二、问题发生的原因
我们先看一下 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 属性值。
接下选取 Value 值相同的 "陈六" 这个选项,也会正常引发 SelectedIndexChanged ,并显示该成员的 SelectedIndex 及 SelectedValue 属性值。
备注:本文同步发布于「第一届iT邦帮忙铁人赛」,如果你觉得这篇文章对您有帮助,记得连上去推鉴此文增加人气 ^^
http://ithelp.ithome.com.tw/question/10013457
http://ithelp.ithome.com.tw/question/10013458