为了比标题更具描述性,我在ASP.NET中有一系列下拉列表,这些下拉列表都填充了同一过程中的相同值.我正在try 找出一种方法来从后续下拉列表中删除值,前提是该值已在上一个列表中被选中.例如,如果用户在"DDL 1"中 Select "Value 1",则"Value 1"不应出现在"DDL 2"或任何其他后续列表(DDL 3、DDL 4等)中. 现在,我的列表由相同的过程填充,我还有另一个过程试图过滤先前 Select 的值.

下拉列表:

<td>
                    <asp:DropDownList ID="ddlEquipmentType1" runat="server" AppendDataBoundItems="true" AutoPostBack="true" Width="200px" />
                    <asp:TextBox runat="server" name="txtIndoorUnit1SerialNumber" Placeholder="Serial #" MaxLength="20" ID="txtIndoorUnit1SerialNumber" Style="width: 200px;" errorName="<%$ Resources:Locale, Global_IndoorUnit1SerialNumber%>" />
                    <span runat="server" id="vldReqIndoorUnit1SerialNumber" style="color: Red;" Visible="False">*</span>
                </td>
            </tr>
            <tr id="trIndoorUnit2SerialNumber" runat="server" visible="false">
                <td style="vertical-align:top">
                    <asp:label runat="server" id="Label30" Text="<%$ Resources:Locale, Global_IndoorUnit2SerialNumber%>" />
                </td>
                <td>
                    <asp:DropDownList ID="ddlEquipmentType2" runat="server" AppendDataBoundItems="true" AutoPostBack="true" Width="200px" />
                    <asp:TextBox runat="server" name="txtIndoorUnit2SerialNumber" Placeholder="Serial #" MaxLength="20" ID="txtIndoorUnit2SerialNumber" Style="width: 200px;" errorName="<%$ Resources:Locale, Global_IndoorUnit2SerialNumber%>" />
                    <span runat="server" id="vldReqIndoorUnit2SerialNumber" style="color: Red;" Visible="False">*</span>
                </td>
            </tr>

(还有更多,但它们都是这样设置的)

填充程序:

Protected Sub PopEquipmentDDLs()
    Dim dt As DataTable = New DataTable
    Dim strSQL As String = "SELECT RecID, EquipmentType FROM EquipmentTypes WHERE RecID IN (SELECT PrimaryEType FROM ETRelationship WHERE RecID = @RecID UNION ALL SELECT AccessoryEType1 FROM ETRelationship WHERE RecID = @RecID UNION ALL SELECT AccessoryEType2 FROM ETRelationship WHERE RecID = @RecID UNION ALL SELECT AccessoryEType3 FROM ETRelationship WHERE RecID = @RecID UNION ALL SELECT AccessoryEType4 FROM ETRelationship WHERE RecID = @RecID UNION ALL SELECT AccessoryEType5 FROM ETRelationship WHERE RecID = @RecID UNION ALL SELECT AccessoryEType6 FROM ETRelationship WHERE RecID = @RecID UNION ALL SELECT AccessoryEType7 FROM ETRelationship WHERE RecID = @RecID UNION ALL SELECT AccessoryEType8 FROM ETRelationship WHERE RecID = @RecID UNION ALL SELECT AccessoryEType9 FROM ETRelationship WHERE RecID = @RecID) AND EquipmentType <> @PEquipmentType"
    Dim ddlCollection As New List(Of DropDownList)() From {ddlEquipmentType1, ddlEquipmentType2, ddlEquipmentType3, ddlEquipmentType4, ddlEquipmentType5, ddlEquipmentType6, ddlEquipmentType7, ddlEquipmentType8, ddlEquipmentType9}
    Dim selectedValues As New Dictionary(Of Integer, String)()

    'For i As Integer = 0 To ddlCollection.Count - 1
    '    Dim ddl As DropDownList = ddlCollection(i)
    '    If ddl.SelectedIndex > 0 Then
    '        selectedValues.Add(i, ddl.SelectedItem.Value)
    '    End If
    'Next

    Using conn As New SqlConnection(ConfigurationManager.ConnectionStrings("WillisConnectionString").ToString)
        Using cmdSQL As New SqlCommand(strSQL, conn)
            cmdSQL.Parameters.Add("RecID", SqlDbType.Int).Value = ddlNbZones.SelectedValue
            cmdSQL.Parameters.Add("PEquipmentType", SqlDbType.VarChar).Value = txtEType.Text
            conn.Open()
            dt.Load(cmdSQL.ExecuteReader())

            'Clear all drop-down lists
            For Each ddl As DropDownList In ddlCollection
                ddl.Items.Clear()
                ddl.Items.Add(New ListItem("---SELECT EQUIPMENT---", "0"))
            Next

            'populate drop-downs
            For i As Integer = 0 To ddlCollection.Count - 1
                Dim ddl As DropDownList = ddlCollection(i)

                ddl.DataSource = dt
                ddl.DataTextField = "EquipmentType"
                ddl.DataValueField = "RecID"
                ddl.DataBind()

                'If selectedValues.ContainsKey(i) Then
                '    ddl.SelectedValue = selectedValues(i)
                'End If
            Next

            For Each item In ddlEquipmentType2.Items
                System.Diagnostics.Debug.WriteLine(item)
            Next
        End Using
    End Using
End Sub

过滤程序:

Private Sub FilterDDLs(ByVal ddlCollection As List(Of DropDownList))
    For i As Integer = 0 To ddlCollection.Count - 2
        Dim ddl As DropDownList = ddlCollection(i)

        ' Skip filtering if selected index is 0
        If ddl.SelectedIndex > 0 Then
            Dim selectedItemText As String = ddl.SelectedItem.Text

            For j As Integer = i + 1 To ddlCollection.Count - 1
                Dim subsequentDDL As DropDownList = ddlCollection(j)

                Dim itemToRemove As ListItem = subsequentDDL.Items.FindByText(selectedItemText)
                If itemToRemove IsNot Nothing Then
                    subsequentDDL.Items.Remove(itemToRemove)
                End If
            Next
        End If
    Next
End Sub

我从下拉列表的数据绑定事件中调用Filter过程,它循环遍历所有列表,并try 基于匹配文本删除项我也try 基于选定的值删除它们,因为它们都是相同的值.

如果我删除"If ddl.SelectedIndex&gt;0 Then"并且不查找索引大于0的DDL,它将删除我在填充过程中添加的值为(0)的ListItem,但无论出于什么原因,我都无法让它删除值1+.

我不确定我是不是在错误的位置调用了这个函数,但我也try 了在"seltedIndexChanged"事件中调用过滤器过程,但都没有成功.

我还try 过过滤填充过程中的列表,然后在每个"seltedIndexChanged"事件中调用POP过程,而不是使用单独的过程.因此,注释代码将所选值存储在字典中,然后在事后重新分配它们.

如能就此问题提供任何帮助,将不胜感激.

推荐答案

好的,那么就说这个标记:

我将使用列表框,但列表框的"事件"模型与组合框(DropDowList)100%相同,并且此代码可以与Listbox或DropDownList互换很多.

因此,标记:

让我们在页面上放置3个列表框.

例.这一点:

        <asp:ListBox ID="ListBox1" runat="server"
            style="float:left"
            DataValueField="ID"
            DataTextField="HotelName"
            OnSelectedIndexChanged="ListBox1_SelectedIndexChanged"
            Height="220px" Width="230"
            AutoPostBack="true"
            SelectionMode="Multiple"
        >
        </asp:ListBox>


        <asp:ListBox ID="ListBox2" runat="server"
            style="float:left;margin-left:20px"
            DataValueField="ID"
            DataTextField="HotelName"
            OnSelectedIndexChanged="ListBox2_SelectedIndexChanged"
            Height="220px" Width="230"
            AutoPostBack="true"
            SelectionMode="Multiple"
        >
        </asp:ListBox>


        <asp:ListBox ID="ListBox3" runat="server"
            style="float:left;margin-left:20px"
            DataValueField="ID"
            DataTextField="HotelName" 
            Height="220px" Width="230"
            SelectionMode="Multiple"
        >
        </asp:ListBox>

要加载的代码:

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

    If Not IsPostBack Then
        LoadCombo
    End If
End Sub

Sub LoadCombo()

    Dim strSQL As String =
        "SELECT ID, HotelName FROM tblHotelsA ORDER BY HotelName"
    Dim rstHotels As DataTable
    rstHotels = MyRst(strSQL)

    ListBox1.DataSource = rstHotels
    ListBox1.DataBind()

    ListBox2.DataSource = rstHotels
    ListBox2.DataBind()

    ListBox3.DataSource = rstHotels
    ListBox3.DataBind()


End Sub

和"级联代码--我们只需要Lb1和Lb2的代码.

所以,这个代码是:

Protected Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs)

    Cascade()

End Sub

Protected Sub ListBox2_SelectedIndexChanged(sender As Object, e As EventArgs)
    Cascade(2)
End Sub

Sub Cascade(Optional lbStart As Integer = 1)

    ' remove all/any selected in 1 from 2
    If lbStart < 2 Then
        ListBox2.Items.Clear()
        For Each LB1 As ListItem In ListBox1.Items
            If Not LB1.Selected Then
                Dim nItem As New ListItem(LB1.Text, LB1.Value)
                ListBox2.Items.Add(nItem)
            End If
        Next
    End If

    ' now remove any selected in 2 from 3

    ListBox3.Items.Clear()
    For Each LB2 As ListItem In ListBox2.Items
        If Not LB2.Selected Then
            Dim nItem As New ListItem(LB2.Text, LB2.Value)
            ListBox3.Items.Add(nItem)
        End If
    Next

End Sub

我们现在看到/得到的是:

enter image description here

当然,现在我使用了帮助器 routine 来加载SQL(只返回一个数据表).这是来self 的全球得心应手的花花公子帮手程序.(对于上面的代码示例来说并不是很重要,但我将作为FYI包含在内

Public Function MyRst(strSQL As String) As DataTable

    Dim rstData As New DataTable
    Using conn As New SqlConnection(My.Settings.TEST4)
        Using cmdSQL As New SqlCommand(strSQL, conn)
            conn.Open()
            rstData.Load(cmdSQL.ExecuteReader)
            rstData.TableName = strSQL
        End Using
    End Using
    Return rstData
End Function

Edit: For the skeptics, above as dropdown list.

因此,使用现有的标记,更改为下拉列表

        <asp:DropDownList ID="ListBox1" runat="server"
            style="float:left"
            DataValueField="ID"
            DataTextField="HotelName"
            OnSelectedIndexChanged="ListBox1_SelectedIndexChanged"
            Width="230"
            AutoPostBack="true"
        >
        </asp:DropDownList>


        <asp:DropDownList ID="ListBox2" runat="server"
            style="float:left;margin-left:20px"
            DataValueField="ID"
            DataTextField="HotelName"
            OnSelectedIndexChanged="ListBox2_SelectedIndexChanged"
            Width="230"
            AutoPostBack="true"
        >
        </asp:DropDownList>


        <asp:DropDownList ID="ListBox3" runat="server"
            style="float:left;margin-left:20px"
            DataValueField="ID"
            DataTextField="HotelName" 
            Width="230"
        >
        </asp:DropDownList>

因此,代码隐藏现在变成了这样:

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

    If Not IsPostBack Then
        LoadCombo
    End If

End Sub

Sub LoadCombo()

    Dim strSQL As String =
        "SELECT ID, HotelName FROM tblHotelsA ORDER BY HotelName"
    Dim rstHotels As DataTable
    rstHotels = MyRst(strSQL)

    ListBox1.DataSource = rstHotels
    ListBox1.DataBind()
    ListBox1.Items.Insert(0, New ListItem("Please Select"))

    'ListBox2.DataSource = rstHotels
    'ListBox2.DataBind()

    'ListBox3.DataSource = rstHotels
    'ListBox3.DataBind()


End Sub

Protected Sub ListBox1_SelectedIndexChanged(sender As Object, e As EventArgs)
    Cascade()
End Sub

Protected Sub ListBox2_SelectedIndexChanged(sender As Object, e As EventArgs)
    Cascade(2)
End Sub

Sub Cascade(Optional lbStart As Integer = 1)

    ' remove all/any selected in 1 from 2
    If lbStart < 2 Then
        ListBox2.Items.Clear()
        For Each LB1 As ListItem In ListBox1.Items
            If Not LB1.Selected Then
                Dim nItem As New ListItem(LB1.Text, LB1.Value)
                ListBox2.Items.Add(nItem)
            End If
        Next
    End If

    'Now remove any selected in 2 from 3

    ListBox3.Items.Clear()
    For Each LB2 As ListItem In ListBox2.Items
        If Not LB2.Selected Then
            Dim nItem As New ListItem(LB2.Text, LB2.Value)
            ListBox3.Items.Add(nItem)
        End If
    Next

End Sub

现在我们看到/得到的是:

enter image description here

当然,这就引出了一个问题,为什么不使用一个带有 Select 器模式="多个"的列表框.

列表框唯一真正的缺点是,用户通常不知道您可以按住CTRL键来 Select 多个项目,因此,这确实需要键盘+鼠标操作.

Asp.net相关问答推荐

如何在 ASP.NET RadioButtonList 中的项目之间添加空格

什么是 SNIReadSyncOverAsync,为什么需要很长时间才能完成?

我可以将图像添加到 ASP.NET 按钮吗?

无法连接到 ASP.Net 开发服务器问题

如何异步渲染局部视图

是否可以访问位于另一个项目中的 MVC 视图?

如何使用 javascript 获取 MVC 应用程序的基本 URL

即使使用正确的 Accepts 标头,WebAPI 也不会返回 XML

AddDefaultTokenProviders:它是什么以及如何使用那些默认提供者?

问题映射 HttpHandler --> HTTP Error 404 Not Found

为什么aspx文件会返回404(找不到页面)

判断邮箱地址是否对 System.Net.Mail.MailAddress 有效

LINQ to Entities 无法识别 MVC 4 中的System.String ToString()方法

ASP.NET Core 2.0 为同一端点结合了 Cookie 和承载授权

ASP.NET IIS Web.config [内部服务器错误]

.NET 4.0 中的自定义 MembershipProvider

MSBuild DeployOnBuild=true 不发布

ASP.Net 无法创建/卷影复制

如何在 asp.net 中实现Access-Control-Allow-Origin标头

可以在不 destruct 站点的情况下将 MIME 类型添加到 web.config 吗?