VBA(Excel)ActiveX列表框更改事件recursion行为

我不是VBA程序员,所以如果我在这个问题上的一些术语是不正确的,我会提前道歉。 我的一个同事想在清单被选中的时候立即清除这个select。 一些谷歌search后,我们发现一个办法是通过更改事件。 最初我们尝试了:

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

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

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

那可行! 虽然我们期望同样的行为 – 对于Selected属性的设置,会导致Change事件再次触发并导致无限循环。

然而,似乎有一次例如ListBox1.Selected(0)= False Change事件被重新触发,但在这个迭代它不重新触发这一行 – 我猜是因为它知道这个Selected属性已经被设置为False对于这个项目,所以没有什么改变。

但是,如果是这样的话,那么我们也会期望在第一个解决scheme中的行为..所以似乎有一些区别说ListBox1.Selected(i)= False与直接指定实际项目索引(而不是通过variablesi )。

有没有人知道这种行为的原因? 希望这个问题是有道理的,我试着尽我所能解释它。

谢谢阿米特

我晚了一年,但我希望这会帮助别人。 我遇到了Listbox1_Click()无限循环而不是change()的问题 。 不过,我认为这可以成为两个可行的解决scheme。

每当我调用Listbox1.Selected(i)= True时 ,它将触发它作为Click()Change() 。 在我的click()例程中,有一些索引会导致整个列表重新填充新列表并重新select自身。 这会导致无限循环重新select自己。 我花了一天的时间来解决问题,但最终解决scheme是不使用click()事件; 相反,我使用MouseDown()事件进行了一些计算。 这消除了使用click() 。 注意到我在单个select列表框中使用这个,而不是多select列表框。 您可以使用带有布尔值的If语句将其应用于多重select。 祝你好运!

 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 

将您的循环附加到click事件而不是更改事件来停止无限循环:

 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 

我不相信这个问题的“为什么”有一个简单的答案。 一般来说,单词是非常线性的,一次只能处理一件事情。 现代处理器,然而,优化代码和串联运行的东西。 Word会以足够可预测的方式混淆,为混淆提供相对一致的解决方法。 你上面提到的这个技巧(这样做两次)是处理这个问题的一个stream行的方法(当我遇到与列表框相同的问题时,我看到很多推荐回来)。

我宁愿增强逻辑,而不是尝试一下马虎的工作。 如上所述,而不是closures事件或使用双重伎俩,我只是意识到,改变事件不是正确的事件使用。

我将不得不做更多的研究,但是我知道,在For...Next循环中,一旦代码碰到Next行,就会将1加到variables上(假设你没有定义一个Step ),然后运行一个逻辑testing,以确定variables是否仍然在你的包容性之间。 由于每个这些运行独立,它允许代码检查您的_Change()事件。 防止无限循环的一些很好的解决方法: Change()事件无限循环

编辑:这是什么导致触发ListBox.Change文档的一些额外的信息。 它提到了这一点

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

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

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

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

所以,我会尝试这两个尝试通过纯编程来模拟Application.EnableEvents工作的替代方法:

  • scheme1:更改事件的一般解决scheme。 但是,如果NoExecutevariables没有被任何原因复位(函数由于错误而退出),那么有点冒险。

     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 
  • scheme2:在这种情况下,最好的解决scheme,虽然不如前一个漂亮。

     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 

让我知道它的工作。

本网站深入介绍了备选scheme1: http : //www.cpearson.com/excel/SuppressChangeInForms.htm

为了阻止无限的疯狂,你可以做到这一点(更难的是更好的问题是,为什么你会这样做,因为在这之后没有价值可以select或实际上可以,但它会立即变回假)。

 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 

现在,无限循环的原因是不可见的,只能猜测,但首先让我们更正假设没有下一个循环的代码块不会导致同样的问题。 它实际上和第一个做同样的事情,如果你改变了ListBox1.Selected(1)或更高版本的值,就会陷入无限循环。

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

为了全面了解发生了什么,你最好联系MS。

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

 ListBox.Enabled = False 

要么

 ListBox.Locked = True 

或两者,当然。

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

  • 如果“ Enabled和“ Locked均为“ 真” ,则控件可以获得焦点,并在表单中正常显示(不显示灰色)。 用户可以复制但不能编辑控件中的数据。
  • 如果EnabledTrue并且LockedFalse ,则控件可以获得焦点并在表单中正常显示。 用户可以复制和编辑控件中的数据。
  • 如果EnabledFalseLockedTrue ,则控件无法获得焦点并在窗体中变暗。 用户既不能复制也不能编辑控件中的数据。
  • 如果EnabledLocked都为False ,则控件无法获得焦点并在窗体中变暗。 用户既不能复制也不能编辑控件中的数据。

请注意,我不确定Office 2007中的行为,因为我尝试了Office 2013的示例,并获得了两个代码片段的完全相同的行为。 他们既不阻止select,而是做“没有”(好,他们被调用两次,但改变不影响显示的select)。

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