【问题标题】:Loading data in to a combo box is slow将数据加载到组合框中很慢
【发布时间】:2009-08-04 23:01:09
【问题描述】:

我有一个带有搜索屏幕的 VB6 应用程序。在搜索中,我有 9 个组合框。有些组合框只有几个项目,但有些组合框有几百个项目。填充数据需要很长时间(几秒钟)。

每个组合框的配置都是一样的:Sorted = False, Style = 2 - Dropdown List

3 个组合框的项目少于 20 个。 1 有 130 个项目。 4 有大约 250 个项目 1 有近 700 项。

我用相似的代码填充所有九个组合框。

While Not RS.EOF

    cmbX.List(i) = RS("Description")
    cmbX.ItemData(i) = RS("Id")

    i = i + 1

    RS.MoveNext
Wend

我尝试设置 Visible = False,但对性能没有影响。

是否有另一种填充组合框的方法比我现有的方法效果更好?

【问题讨论】:

    标签: performance vb6 combobox


    【解决方案1】:

    您可以尝试以下方法。根据this post,您可以使用 Windows API 函数来填充组合框,而不是通常的 AddItem 方法,从而减少大约 60% 的开销:

    Private Const CB_ERR As Long = -1
    Private Const CB_ADDSTRING As Long = &H143
    Private Const CB_RESETCONTENT As Long = &H14B
    Private Const CB_SETITEMDATA As Long = &H151
    
    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
    hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
    
    Public Sub AddItem(cmb As ComboBox, Text As Variant, Optional ItemData As Long)
    
       Dim l As Long
       Dim s As String
    
       If VarType(Text) = vbString Then
          s = Text
       Else
          s = Trim$(Str$(Text))
       End If
    
       l = SendMessage(cmb.hwnd, CB_ADDSTRING, 0&, ByVal s)
       If l <> CB_ERR Then
          SendMessage cmb.hwnd, CB_SETITEMDATA, l, ByVal ItemData
       End If
    
    End Sub
    
    Public Sub Clear(cmb As ComboBox)
       SendMessage cmb.hwnd, CB_RESETCONTENT, 0, 0&
    End Sub
    

    您可以省略函数调用,直接调用 API 函数,这样可以节省更多。

    【讨论】:

    • +1。 @G Mastros,你能告诉我们你这样做的时间吗?你说几秒钟前。 (时间将非常依赖于机器和操作系统,但看到一个数字仍然很有趣。)
    • 我还没有为所有的组合框实现这个代码。对于其中一个,最初需要 0.2 秒。添加块将其降低到 0.17 秒。使用 API 调用设置数据将其缩短到 0.07 秒。
    【解决方案2】:

    您确实需要重新考虑您的设计。没有用户会想要在组合框中的 700 个项目之间进行选择。如果你不纠正它,它会让你的应用程序看起来很混乱。

    当我听到这样的情况时,脑海中总会浮现出一幅画面:

    【讨论】:

    【解决方案3】:

    一些建议:

    • 使用With RS

    • 使用 Recordset 对象的RecordCount 属性,而不是在每次迭代时测试EOF(如果RecordCount = -1,那么您应该更改游标类型、游标位置等以确保支持RecordCount)。

    • 使用For..Next 循环而不是维护您自己的迭代器变量。

    • 使用 bang 运算符 (!)。

    例如:

    With RS
    
      Debug.Assert .RecordCount >= 0
    
      Dim counter As Long
      For counter = 0 To .RecordCount - 1
    
        cmbX.List(counter) = !Description
        cmbX.ItemData(counter) = !Id
    
        .MoveNext
      Next
    
    End With
    

    也许要考虑的其他事情是将组合的Sorted 属性设置为False,如果需要排序,则使用Recordset 的Sort 属性或在源处进行排序(例如,在SQL 中使用ORDER BY 子句代码)。

    【讨论】:

    • sorted 已经设置为 false (如我原来的帖子中所述)。使用 with 块确实加快了速度,但并没有太大的区别。不过我很欣赏这个建议。谢谢。
    【解决方案4】:

    您可以尝试在添加新项目时告诉组合框不要重新绘制自身。您可以使用WM_SETREDRAW 执行此操作。编辑 - 显然这没有帮助,可能是因为组合框在填充时被隐藏,这可能会给您带来所有相同的好处。

      Private Declare Function SendMessage Lib "user32" Alias "SendMessageA"( _
        ByVal hwnd As Long, ByVal wMsg As Long, _
        ByVal wParam As Long, lParam As Any) As Long
      Private Const WM_SETREDRAW = &HB
    
      Call SendMessage(cmbX.hWnd, WM_SETREDRAW, 0, 0&)
      'DO STUFF'
      Call SendMessage(cmbX.hwnd, WM_SETREDRAW, 1, 0&)
    

    警告:很多其他优秀的 VB6 网站 tell 你改用 LockWindowUpdate。不要这样做,否则你会get bugs。还有对Raymond Chen的嫌弃!

    【讨论】:

    【解决方案5】:

    为什么要预先填充列表,然后重新填充 List(i) 和 ItemData(i)?相反,您应该执行以下操作

    While Not RS.EOF
    
        cmbX.AddItem RS("Description")
        cmbX.ItemData(cmbX.NewIndex) = RS("Id")
    
        RS.MoveNext
    Wend
    

    您将看到 Robert Harvey 的答案与上面的代码之间的性能差异为零。为了提高速度,请应用 MarkJ 的答案。

    另一个问题可能是您用于从数据库中取回数据的游标。由于您要从记录集中加载每一项,因此几乎没有理由使用服务器端游标。因此,您可能希望在检索记录集时指定客户端光标。

    【讨论】:

    • 感谢您的建议。我没有预先填充列表以及重新填充 list(i) 和 ItemData(i)。 AddItem 比设置列表属性慢很多倍。你的建议比罗伯特哈维的慢很多倍。事实上,它的速度慢了 10 倍。我已经在使用客户端游标了,问题不在于数据库。
    • 好的,也许不是预填充,但您要先创建行,然后再填充它们。简而言之,你这样做: cmbX.AddItem "" cmbX.List(i) = "blah" cmbX.ItemDate(i) = 123 我不确定你的方式是否更快
    【解决方案6】:

    这确实看起来很长。你是如何打开你的记录集的?您是否在使用 firehose(只读、只进记录集)游标?如果不是,您可能会从中获得一些性能改进。确保您的 SQL 语句仅返回组合框所需的数据(即不要使用 SELECT *)。

    如果您的 SQL 语句包含 WHERE 子句或 JOINS,请确保您在适当的字段上有索引。

    如果您使用 ACCESS 作为后端,您将通过升级到 SQL Server Express 来立即提高速度。

    【讨论】:

    • 感谢您的有用评论!我正在使用 SQL Server。所有查询都已尽我所能进行优化。事实上,我正在从一个存储过程返回多个记录集。存储过程在 SQL Server 查询窗口中运行 50 毫秒。它在大约 200 毫秒内返回到 vb。我觉得这不是数据库问题。
    【解决方案7】:

    '' 填充组合框

    我必须等待 20 秒才能让我的程序在城市组合框中填充 80,000 条记录 我尝试了几种方法,但都更糟

    这是我的原始代码

        Me.txt_City.DataSource = tblCities
        Me.txt_City.ValueMember = "city"
        Me.txt_City.DisplayMember = "city"
    

    你猜怎么着,我只是把第一行代码移到最后 然后人口只用了5秒

        Me.txt_City.ValueMember = "city"
        Me.txt_City.DisplayMember = "city"
    
        Me.txt_City.DataSource = tblCities
    

    试试看

    【讨论】:

      猜你喜欢
      • 2021-12-24
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 2016-02-04
      • 2012-10-08
      • 1970-01-01
      • 2013-09-29
      • 1970-01-01
      相关资源
      最近更新 更多