VBA(Excel)ActiveX ListBox更改事件递归行为

时间:2014-11-25 13:30:12

标签: excel vba listbox

我不是VBA程序员,所以如果我在这个问题上的一些术语不正确,我会提前道歉。我的一位同事希望在选择后立即从列表框中清除选择。经过一些谷歌搜索,我们发现一种方法是通过Change事件。最初我们尝试过:

Private Sub ListBox1_Change()
    For i = 0 To ListBox1.ListCount - 1
        ListBox1.Selected(i) = False
    Next i
End Sub

但是,似乎将所选属性设置为False会在列表框中触发更改事件,这实际上会成为无限循环并导致Excel(2007)崩溃。鉴于我们知道有两个条目我们也尝试过:

Private Sub ListBox1_Change()
    ListBox1.Selected(0) = False
    ListBox1.Selected(1) = False
End Sub

这很有效!虽然我们期望相同的行为 - 设置 Selected 属性会导致Change事件再次触发并获得无限循环。

但似乎有一次,例如 ListBox1.Selected(0)= False 更改事件被重新触发,但在该次迭代中它不会重新触发此行 - 我猜是因为它知道此已选择该项目的属性已设置为 False ,因此没有任何变化。

但如果是这种情况,那么我们也会在第一个解决方案中期待这种行为..所以看来 ListBox1.Selected(i)= False有一些区别与直接指定实际项目索引(而不是通过变量 i )。

有谁知道这种行为的原因?希望这个问题有意义,我试图尽可能地解释它。

由于   阿米特

6 个答案:

答案 0 :(得分:1)

我已经迟到了一年,但我希望这会对其他人有所帮助。我遇到了 Listbox1_Click()无限循环而不是更改()的问题。但是,我认为这对两者都是可行的解决方案。

每当我拨打 Listbox1.Selected(i)= True 时,它会将其作为 Click()更改()触发。在我的 click()例程中,有一些索引会导致整个列表重新填充自己的新列表和重新选择本身。这会在重新选择自身时导致无限循环。我花了一天时间进行故障排除,但最终解决方案是不使用 click()事件;相反,我使用 MouseDown()事件进行了一些计算。这消除了使用 click()。注意到我在单个选择列表框中使用它而不是多选列表框。您可以使用带有布尔值的 If 语句将其应用于multiselect。古德勒克!

Private Sub ListBox1_MouseDown(ByVal Button As Integer, ByVal Shift As 

Integer, ByVal x As Single, ByVal Y As Single)

    On Error Resume Next  'You can comment this out for trouble shooting
    If Button = 1 And UBound(ListBox1.List) <> -1 Then
        ListBox1.Selected(((Y / 9.75) - 0.5) + ListBox1.TopIndex) = True
        MsgBox "left Click"
        'You can use Button = 2 for right click
        'Do some other stuff including listbox1.select(1234)

    End If
End Sub

答案 1 :(得分:0)

将循环附加到click事件而不是change事件以停止无限循环:

Private Sub ListBox1_Click()
'Do the stuff you want to do based on the selection

'Clear selections
For i = 0 To ListBox1.ListCount - 1
    ListBox1.Selected(i) = False
Next i
End Sub

我不相信这个问题的“原因”有一个简单的答案。通常,单词是非常线性的,一次只能处理1个东西。然而,现代处理器可以优化代码并同时运行。 Word简单地以可预测的方式混淆,为混淆提供相对一致的变通方法。你上面提到的技巧(做两次)是处理这个问题的一种流行方式(当我遇到与列表框相同的问题时,我看到了很多推荐)。

我更喜欢增强逻辑而不是尝试草率的工作。如上所述,而不是关闭事件或使用双重技巧,我只是意识到更改事件不是正确使用的事件。

答案 2 :(得分:0)

我必须对此进行更多的研究,但我知道在For...Next循环期间,一旦代码点击Next行,它就会向变量添加1(假设你没有定义了Step),然后运行逻辑测试以确定变量是否仍在您的包容性For blah条件之间。因为每个都独立运行,所以它允许代码检查您的_Change()事件。防止无限循环的一些很好的解决方法:Change() Event Infinite Loops

编辑:以下是有关导致触发ListBox.Change Documentation的原因的一些其他信息。它提到了

  

此外,对ListBox.ListIndex的编程更改也会触发Change事件。

可能是循环之间正在检查的内容。

答案 3 :(得分:0)

你正确使用Change事件,但它在ListBoxes中有点棘手。每次取消选择元素时,您显然会触发Change事件,因为您正在从代码中更改 ListBox。

来自 guitarthrower 的答案朝着良好的方向发展,但我不认为Application.EnableEvents会在这种情况下完成工作,因为它对ActiveX没有任何影响对象。

所以,我会尝试这两个尝试通过简单编程模仿Application.EnableEvents的工作的替代方案:

  • 备选方案1:Change事件的一般解决方案。但是,如果NoExecute变量由于任何原因而没有重置(由于错误退出函数),那么风险会很大。

    Private NoExecute As Boolean
    
    Private Sub ListBox1_Change()
    
        If NoExecute Then Exit Sub
    
        NoExecute = True
    
        For i = 0 To ListBox1.ListCount - 1
            ListBox1.Selected(i) = False
        Next
    
        NoExecute = False
    
    End Sub
    
  • 备选方案2:在这种情况下的最佳解决方案,虽然不如前一个那么漂亮。

    Private Sub ListBox1_Change()
        For i = 0 To ListBox1.ListCount - 1
            If ListBox1.Selected(i) Then
                ListBox1.Selected(i) = False
            End If
        Next
    End Sub        
    

让我知道它是否有效。

本网站深入介绍备选方案1:http://www.cpearson.com/excel/SuppressChangeInForms.htm

答案 4 :(得分:0)

为了阻止无限的疯狂,你可以做到这一点(更难的更好的问题是,为什么你会这样做,因为在此之后没有价值可以选择或实际可以,但它立即改回到错误) -

Private Sub ListBox1_Change()
    For i = 0 To ListBox1.ListCount - 1 
         if ListBox1.Selected(i) = true then ListBox1.Selected(i) = false 
    Next i
End Sub

现在无限循环的原因是不可见的,人们只能猜测,但首先让我们纠正没有for for next循环的代码块不会导致同样问题的假设。它实际上与第一个相同并且陷入无限循环,如果您更改ListBox1.Selected(1)或更高版本的值..

为什么会发生这种情况,更改事件将在第一个值更改后立即触发,并且随着第一个单元格的更改,更改事件将被触发,代码将针对该实例停止执行并再次开始。现在两个代码都工作而不会导致循环,如果只改变第一个项目,为什么会发生这种情况可能是因为编译器开始使用它的自动化。我不能告诉你发生事件背后的真正原因,但可能发生的事情是编译器预测何时只改变第一个值并优化代码只做一个改变而没有别的什么,当其他值改变时它是尝试重写每个值和第一个值,每次都会触发无限循环。

只是如此,为了更全面地了解幕后发生的事情,你最好联系MS。

答案 5 :(得分:0)

仅供将来参考:如果您只想在ListBox控件中禁用选择,则可以使用

ListBox.Enabled = False

ListBox.Locked = True

或两者,当然。

您可以在MSDN上找到有关行为差异的详细信息 基本上它应该是这样的(直接来自上面链接的MSDN文章):

  • 如果EnabledLocked都是 True ,则控件可以获得焦点并在表单中正常显示(不会变暗)。用户可以复制但不能编辑控件中的数据。
  • 如果Enabled True Locked False ,则控件可以获得焦点并正常显示在表单中。用户可以在控件中复制和编辑数据。
  • 如果Enabled 错误Locked True ,则控件无法获得焦点并在表单中变暗。用户既不能复制也不能编辑控件中的数据。
  • 如果EnabledLocked都是 False ,则控件无法获得焦点并在表单中变暗。用户既不能复制也不能编辑控件中的数据。

请注意,自从我在Office 2013中尝试使用您的示例后,我不确定Office 2007中的行为,并为两个代码段获取完全相同的行为。并且他们都阻止选择,而是做什么&#34;没有&#34; (好吧,它们会被调用两次,但改变不会影响显示的选择)。

在我的实验中,我也注意到ListBox_Change事件在ListBox_Click事件之前被调用。