动态填充ListBox vb.net的有效方法

时间:2019-10-03 15:54:37

标签: vb.net dynamic listbox

我有一个程序,允许用户按名称搜索客户。到目前为止,我这样做的方式(下面的代码)是让用户开始在TextBox(tbCust)中键入客户名称,代码在TextChanged事件上触发,并根据用户键入的内容重新填充ListBox。我认为这里的想法很明显并且很常用。

这在我的计算机上没有出现最小延迟的情况,但是在其他一些用户(这些计算机是基本级别的计算机)上,更新之间存在100ms到300ms的延迟,这给用户带来了非常糟糕的体验。

如果我在这里错了,请纠正我,但是我觉得应该可以轻松实现此功能,而几乎没有任何计算机滞后。

我认为有一种更正确/更有效的方法,使我不够聪明,无法独自提出(输入,所有人!)

请提供一些可能更“合适”的方法,以提高性能。我认为我的问题出在例程每次运行时都要查询数据库(每次用户键入一个字母),但是我不确定在仍然使用实时数据时该如何做。

非常感谢!

我的计算机上性能可接受的视频:Youtube Video #1

用户计算机上的视频表现不可接受:YouTube Video #2

用户计算机规格:User Computer Specs

    Private Sub tbCust_TextChanged(sender As Object, e As EventArgs) Handles tbCust.TextChanged
    'This populates the Customer Selection list box with customers whose names start with the
    'string of letters in the customer name text box.


    If tbCust.TextLength > 0 Then
        lbCustSelect.Visible = True
        Dim SQL As String
        SQL = "SELECT C_CUSTOMER as ID, C_SHIPNAME as Name FROM CUSTOMER WHERE LEFT(C_SHIPNAME," & tbCust.TextLength & ") ='" & tbCust.Text & "'"
        'Query Database
        AeroDBcon.RunQuery(SQL)

        'Fill DataTable with Query results
        dtCustomers = AeroDBcon.DBds.Tables(0)

        'Tie DataTable to ListBox
        lbCustSelect.DataSource = dtCustomers
        lbCustSelect.DisplayMember = "Name"
        lbCustSelect.ValueMember = "ID"

        'If there are no results, hide the ListBox
        If dtCustomers.Rows.Count = 0 Then
            lbCustSelect.Visible = False
        End If
    Else
        'if there is no text in the customer name text box, hide the listbox
        lbCustSelect.Visible = False
    End If
End Sub

1 个答案:

答案 0 :(得分:0)

在SQL中进行筛选通常比在客户端进行筛选更快。但是,由于表CUSTOMER可能没有那么大,而且查询数据库似乎存在开销问题,因此让我们一次查询所有数据,然后在客户端进行过滤。

我喜欢强打字。即使您不使用ORM,我们仍然可以创建一个类来保存您的结果:

Private Class Customer
    Public Property ID As String
    Public Property Name As String
End Class

如果我们拥有所有客户的集合,

Private customers As IEnumerable(Of Customer)

像这样简单地过滤

Dim filteredCustomers = customers.Where(Function(c) c.Name.StartsWith(filterString)).ToList()

此外,我不会在按键上运行查询。我也不会在UI线程上运行它(UI事件处理程序在UI上运行,这将导致您的UI在查询运行时冻结)。自上次按键以来经过一定时间后,运行查询,然后在用户界面之外运行查询。 System.Threading.Timer十分适合。

Private ReadOnly queryTimer As New System.Threading.Timer(AddressOf executeQuery, Nothing, -1, -1)
Private ReadOnly keyPressDelay As Integer = 100
Private customers As IEnumerable(Of Customer)
Private filterString As String = ""

Private Sub tbCust_TextChanged(sender As Object, e As EventArgs) Handles tbCust.TextChanged
    filterString = tbCust.Text
    lbCustSelect.Visible = filterString.Length > 0
    If filterString.Length > 0 Then queryTimer.Change(keyPressDelay, -1)
End Sub

Private Sub executeQuery(state As Object)
    ' this could alternately be run in Form_Load
    If customers Is Nothing Then
        Dim sql = "SELECT C_CUSTOMER as ID, C_SHIPNAME as Name FROM CUSTOMER"
        AeroDBCon.RunQuery(sql)
        customers =
            AeroDBCon.DBds.Tables(0).
            AsEnumerable().Select(Function(dr) New Customer With {.ID = dr("ID").ToString(), .Name = dr("Name").ToString()})
    End If

    Dim filteredCustomers = customers.Where(Function(c) c.Name.StartsWith(filterString)).ToList()
    ' Dim filteredCustomers = customers.Where(Function(c) c.Name.Contains(filterString)).ToList()

    ' update the control on the UI thread
    lbCustSelect.Invoke(
        Sub()
            lbCustSelect.DataSource = Nothing
            lbCustSelect.DisplayMember = "Name"
            lbCustSelect.ValueMember = "ID"
            lbCustSelect.DataSource = filteredCustomers
        End Sub)
End Sub

您还应该在处置表单时处置计时器。修改您的Dispose方法为此

Protected Overrides Sub Dispose(disposing As Boolean)
    Try
        If disposing Then
            components?.Dispose()
            queryTimer?.Dispose()
        End If
    Finally
        MyBase.Dispose(disposing)
    End Try
End Sub