我有一个程序,允许用户按名称搜索客户。到目前为止,我这样做的方式(下面的代码)是让用户开始在TextBox(tbCust)中键入客户名称,代码在TextChanged事件上触发,并根据用户键入的内容重新填充ListBox。我认为这里的想法很明显并且很常用。
这在我的计算机上没有出现最小延迟的情况,但是在其他一些用户(这些计算机是基本级别的计算机)上,更新之间存在100ms到300ms的延迟,这给用户带来了非常糟糕的体验。
如果我在这里错了,请纠正我,但是我觉得应该可以轻松实现此功能,而几乎没有任何计算机滞后。
我认为有一种更正确/更有效的方法,使我不够聪明,无法独自提出(输入,所有人!)
请提供一些可能更“合适”的方法,以提高性能。我认为我的问题出在例程每次运行时都要查询数据库(每次用户键入一个字母),但是我不确定在仍然使用实时数据时该如何做。
非常感谢!
我的计算机上性能可接受的视频:Youtube Video #1
用户计算机上的视频表现不可接受:YouTube Video #2
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
答案 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