有没有办法在排序项目后保持列表框的索引顺序?

Is there a way to keep the index order of a listbox after sorting items?

对于我的课程作业,我在 Visual Basic 中制作了一个预订系统,该系统还存储有关未付款项的数据,我使用列表框显示使用列表框项的索引保存在数组中的注册客户的姓名结合数组的主键,例如:

数组:

User ID Amount Owed
0 100
1 0
2 200

然后将其添加到列表框中,客户数据(如姓名和联系信息)存储在发票数据的单独数组中,通过用户 ID 作为主键链接。

列表框:

Index Name
0 John Doe
1 Jane Doe
2 Joe Bloggs

这个系统一切正常,直到我想添加一个复选框来过滤掉没有未付款的人 筛选列表框:

Index Name User ID
0 John Doe 0
1 Joe Bloggs 2

如您所见,列表框的索引现在不再与用户 ID 相同,这反过来又破坏了涉及使用 .SelectedIndex 函数的其他操作。

这里是这个过程中涉及的代码

Private Sub SortOpenInvoice_CheckedChanged(sender as Object, e as EventArgs) _
    Handles CheckBox1.CheckedChanged

    Dim HasOpenInvoice As Boolean

    HasOpenInvoice = False
    If SortOpenInvoice.Checked = True Then
        For i = 0 To numofCustomers - 1
            For j = 0 To numofInvoices - 1
                If invoices(j).AmountOwed > 0 And invoices(j).CustomerID = customers(i).CustomerID Then 'Searches for an open invoice (outstanding payment) linked to a customers ID
                    HasOpenInvoice = True  'Flag for if there is an open invoice (outstanding payment)
                End If
            Next
            If HasOpenInvoice = False Then
                lbxMemberList.Items.RemoveAt(Convert.ToInt32(customers(i).CustomerID)) 'Removes index if no open invoice is found
            Else
                HasOpenInvoice = False
            End If
        Next
    ...
End Sub

过滤掉特定字段后,如何保留索引值?或者您是否可以建议任何其他替代解决方案?

谢谢!

我建议使用另一种方法。

将ListBox的Sorted 属性设置为True并将客户列表分配给ListBox的DataSource 属性。

现在,在 ListBox 中,客户显示为已排序,但他们在列表中仍未排序。我还建议将列表包装在 BindingList 中。它的优点是在您添加或删除客户时自动更新列表框。

您还必须重写客户 class 的 ToString 方法以使 ListBox 以适当的方式显示它们:

Class Customer
    Public Property UserID As Integer
    Public Property AmountOwed As Decimal
    Public Property Name As String

    Public Overrides Function ToString() As String
        Return $"{Name} {UserID}, $={AmountOwed:n2}"
    End Function
End Class

在表格中(作为例子):

Private customerBindingList As BindingList(Of Customer)

Protected Overrides Sub OnLoad(e As EventArgs)
    MyBase.OnLoad(e)

    ' Set up example list of customers
    Dim customers = New List(Of Customer) From {
        New Customer With {.UserID = 0, .Name = "John Doe", .AmountOwed = 100},
        New Customer With {.UserID = 1, .Name = "Jane Doe", .AmountOwed = 0},
        New Customer With {.UserID = 2, .Name = "Joe Bloggs", .AmountOwed = 200}
    }

    ' Use the customers as data source of the ListBox via a BindingList.
    customerBindingList = New BindingList(Of Customer)(customers)
    ListBox1.DataSource = customerBindingList
End Sub

想法是使用 customerBindingList 而不是使用 ListBox。从 ListBox 中,您可以通过 SelectedItem 属性 获取选定的客户。这比使用索引要健壮得多。无论如何,您无法使 UserID 与任何索引保持同步。 UserID 必须保持不变,以便您可以可靠地识别客户,而索引会在添加或删除(或排序)客户时发生变化。

不使用索引的删除函数示例:

Private Sub DeleteButton_Click(sender As Object, e As EventArgs) Handles DeleteButton.Click
    Dim customer = DirectCast(ListBox1.SelectedItem, Customer)

    customerBindingList.Remove(customer)
End Sub

您还可以将 ListBox 的 DisplayMember 属性 设置为“名称”,而不是覆盖 ToString.

您可以将ListBox的ValueMember 属性设置为“UserID”。这使您可以使用

访问用户 ID
Dim id As Integer = DirectCast(ListBox1.SelectedValue, Integer)