VBA:ListBox 更改事件触发两次

huangapple go评论101阅读模式
英文:

VBA: ListBox Change event firing twice

问题

我在Excel中有一个用户表单,在其中问题在一个Listbox控件中进行了索引。单击Listbox中的项目会调用Change事件,根据所选的项目填充其他控件的值。

用户可以更改文本框中的值。在更改它们后,会为该问题设置一个"已保存"标志为False。然后,用户可以将问题保存到内存中,或导航离开该问题。

如果用户在不保存的情况下导航(通过单击Listbox中的不同项目),我想向他们显示警告,提供以下选项:要么放弃未保存的更改,要么保留当前选择,并还原刚刚单击的Listbox选择。

如果选择"放弃更改",那么一切正常。但是当我尝试还原Listbox选择时,它会遇到问题。我使用一个"EventsOn"布尔值来处理何时应该继续执行Change过程,以避免它调用自身。这似乎在代码中的正确点起作用。 但是在EventsOn被恢复后,在Exit Sub之后,似乎Change事件再次被调用。

我不知道为什么事件会再次触发。这导致用户第二次看到这个选项。

以下是我代码的相关部分:

  1. Option Explicit
  2. Dim NumberOfQuestions As Long
  3. Dim EventsOn As Boolean
  4. Dim SelectedListIndex As Long, CurrentQuestion As Long, QuestionSaved As Variant
  5. Private Sub UserForm_Initialize()
  6. ' 省略了大量代码。基本上打开记录集并加载值
  7. ReDim QuestionSaved(1 To NumberOfQuestions) As Boolean
  8. '
  9. For X = 1 To NumberOfQuestions
  10. lbox_QuestionList.AddItem "Question " & X ' Populate the listbox with items
  11. QuestionSaved(X) = True ' Flag the initial state as saved, for each question
  12. If Not X = rst.RecordCount Then rst.MoveNext
  13. Next X
  14. '
  15. ' 选择第一个问题为默认。请注意,Listbox ListIndex0开始,而问题从1开始
  16. SelectedListIndex = 0
  17. CurrentQuestion = 1
  18. EventsOn = True
  19. lbox_QuestionList.ListIndex = SelectedListIndex
  20. End Sub
  21. Private Sub lbox_QuestionList_Change()
  22. ' 确保此事件在以编程方式更改时不会不断触发
  23. If Not EventsOn Then Exit Sub
  24. '
  25. If Not QuestionSaved(CurrentQuestion) Then
  26. If MsgBox(Prompt:="Abandon changes to current question?", Title:="Current question not saved", Buttons:=vbYesNo + vbDefaultButton2) = vbYes Then
  27. ' 放弃更改 = 是
  28. ' 标记为已保存
  29. QuestionSaved(CurrentQuestion) = True
  30. ' 然后继续正常更改
  31. ' (如果用户回到这个问题,它将以原始形式重新加载到内存中)
  32. ' 这部分正常工作
  33. Else
  34. ' 放弃更改 =
  35. EventsOn = False ' 所以这个子过程不会再次被调用
  36. ' 还原ListBox选择。通过重新调用当前问题编号并应用于ListIndex来实现
  37. SelectedListIndex = CurrentQuestion - 1 ' 请记住索引将减1,因为索引从0开始
  38. lbox_QuestionList.ListIndex = SelectedListIndex
  39. EventsOn = True
  40. Exit Sub ' 这应该是结束但不知何故它并没有...
  41. End If
  42. End If
  43. ' 根据新选择的ListIndex继续加载新问题
  44. SelectedListIndex = lbox_QuestionList.ListIndex ' 认识当前选择
  45. ' ListIndex从零开始,所以我们需要加1
  46. CurrentQuestion = SelectedListIndex + 1
  47. ShowQuestion CurrentQuestion
  48. End Sub
  49. Private Sub ShowQuestion(QuestionNumber As Long)
  50. ' 为简洁起见,省略了详细信息。基本上从类的字典中加载详细信息,并填充到文本框中
  51. End Sub
  52. Private Sub cb_Save_Click()
  53. ' 省略了代码。获取当前文本框的值并将其保存到字典中的类中
  54. ' 将当前问题标记为已保存:
  55. QuestionSaved(CurrentQuestion) = True
  56. End Sub
  57. ' 事件处理程序
  58. Private Sub tb_Question_Change()
  59. DoChange
  60. End Sub
  61. ' 几个其他表单控件有类似的事件:都调用下面的"DoChange"
  62. Private Sub DoChange()
  63. If Not EventsOn Then Exit Sub
  64. QuestionSaved(CurrentQuestion) = False ' 如果对表单值进行任何更改,将当前问题标记为未保存
  65. End Sub

我自然而然地搜索了这个问题,但目前还没有答案可以帮助我:

我的代码逻辑似乎是正确的。谜底是为什么Change事件会再次被调用,即使在Exit Sub之后。

英文:

I have a User Form in Excel in which questions are indexed in a Listbox control. Clicking on an item in the Listbox calls a Change event which populates other controls' values according to which item has been selected.

The user may change values within the text boxes. Upon changing them, a "Saved" flag gets set to False for that question. The user may then save the question into memory; or navigate away from the question.

If the user navigates away without saving (by means of clicking a different item in the Listbox), I want to present them with a warning - giving the option to either abandon their unsaved changes; or to remain with the current selection, and revert the Listbox selection which they just clicked.

If "Abandon changes" is selected, it works fine. However it runs into trouble when I try to revert the Listbox selection. I use an "EventsOn" Boolean to handle when the Change procedure should proceed, to avoid it calling itself. This seems to work, at the correct point in the code. However after EventsOn is reinstated, and after Exit Sub, it seems that the Change event is called again.

I do not know why the event is firing again. This results in the user being presented with the option a second time.

A lot of the following code has been stripped out because it relates to details of other form controls; loading/saving data from a database; and handling classes and dictionaries. However I have retained the relevant logic of the form controls:

  1. Option Explicit
  2. Dim NumberOfQuestions As Long
  3. Dim EventsOn As Boolean
  4. Dim SelectedListIndex As Long, CurrentQuestion As Long, QuestionSaved As Variant
  5. Private Sub UserForm_Initialize()
  6. ' Stripped out lots of code here. Basically opens a recordset and loads values
  7. ReDim QuestionSaved(1 To NumberOfQuestions) As Boolean
  8. '
  9. For X = 1 To NumberOfQuestions
  10. lbox_QuestionList.AddItem "Question " & X ' Populate the listbox with items
  11. QuestionSaved(X) = True ' Flag the initial state as saved, for each question
  12. If Not X = rst.RecordCount Then rst.MoveNext
  13. Next X
  14. '
  15. ' Select the first question by default. Note that the Listbox ListIndex starts at 0, whereas the questions start at 1
  16. SelectedListIndex = 0
  17. CurrentQuestion = 1
  18. EventsOn = True
  19. lbox_QuestionList.ListIndex = SelectedListIndex
  20. End Sub
  21. Private Sub lbox_QuestionList_Change()
  22. ' Ensure this event does NOT keep firing in a loop, when changed programmatically
  23. If Not EventsOn Then Exit Sub
  24. '
  25. If Not QuestionSaved(CurrentQuestion) Then
  26. If MsgBox(Prompt:="Abandon changes to current question?", Title:="Current question not saved", Buttons:=vbYesNo + vbDefaultButton2) = vbYes Then
  27. ' Abandon changes = Yes
  28. ' Mark as saved
  29. QuestionSaved(CurrentQuestion) = True
  30. ' Then proceed to change as normal
  31. ' (If the user comes back to this question, it will be re-loaded from memory in its original form)
  32. ' This works okay
  33. Else
  34. ' Abandon changes = No
  35. EventsOn = False ' So this sub is not called again
  36. ' REVERT the ListBox selection. Do this by recalling the current question number, and applying it to the ListIndex
  37. SelectedListIndex = CurrentQuestion - 1 ' Remember that the index will be minus 1, due to indexing starting at 0
  38. lbox_QuestionList.ListIndex = SelectedListIndex
  39. EventsOn = True
  40. Exit Sub ' This should be the end of it. But somehow, it's not...
  41. End If
  42. End If
  43. ' Proceed with loading a new question according to the new selected ListIndex
  44. SelectedListIndex = lbox_QuestionList.ListIndex ' Recognise the current selection
  45. ' ListIndex starts at zero, so we need to add 1
  46. CurrentQuestion = SelectedListIndex + 1
  47. ShowQuestion CurrentQuestion
  48. End Sub
  49. Private Sub ShowQuestion(QuestionNumber As Long)
  50. ' Stripped out code for brevity. Basically loads details from a dictionary of classes, and populates into textboxes
  51. End Sub
  52. Private Sub cb_Save_Click()
  53. ' Stipped out code. Takes values of current text boxes and saves them into a class in a dictionary
  54. ' Mark the current question as saved:
  55. QuestionSaved(CurrentQuestion) = True
  56. End Sub
  57. ''''''''''' Event handlers ''''''''''''''
  58. Private Sub tb_Question_Change()
  59. DoChange
  60. End Sub
  61. ' Several other form controls have similar events: all calling "DoChange" as below
  62. Private Sub DoChange()
  63. If Not EventsOn Then Exit Sub
  64. QuestionSaved(CurrentQuestion) = False ' Flag the current question as NOT saved, if any changes are made to form values
  65. End Sub

Naturally, I have searched for this problem - but there are no answers so far which have assisted me:

The logic of my code seems sound. The mystery is why the Change event is being called a second time, even after Exit Sub.

答案1

得分: 2

在查看了一段时间后,似乎从列表框的更改事件中设置其自己的列表索引(实际上是递归调用它)会引发一些奇怪的后端问题。幸运的是,通过将这部分移到自己的函数中进行迁移,可以轻松解决这个问题。经过一些实验,最好的方法是创建一个函数来清除并重新填充列表框,因此请在您的UserForm代码中创建这个函数:

  1. Private Function PopulateListbox(Optional ByVal arg_lSelected As Long = -1)
  2. Me.lbox_QuestionList.Clear
  3. Dim X As Long '
  4. For X = 1 To NumberofQuestions
  5. lbox_QuestionList.AddItem "Question " & X ' 填充列表框项
  6. QuestionSaved(X) = True ' 对于每个问题,标记初始状态为已保存
  7. ' 如果不是X = rst.RecordCount Then rst.MoveNext
  8. Next X
  9. Me.lbox_QuestionList.ListIndex = arg_lSelected
  10. End Function

现在调整您的初始化事件如下所示(请注意,您需要在此处定义NumberofQuestions,然后在最后调用新函数以填充列表框并选择第一个条目):

  1. Private Sub UserForm_Initialize()
  2. ' 在这里剥离了大量的代码。基本上是打开记录集并加载值
  3. NumberofQuestions = 3 ' 这是NumberofQuestions的定义位置
  4. ReDim QuestionSaved(1 To NumberofQuestions)
  5. ReDim aAnswers(1 To NumberofQuestions)
  6. '
  7. ' 默认情况下选择第一个问题。请注意,列表框的ListIndex0开始,而问题从1开始
  8. SelectedListIndex = 0
  9. CurrentQuestion = 1
  10. EventsOn = True
  11. PopulateListbox SelectedListIndex ' 调用新函数并设置第一个选择
  12. End Sub

最后,将您的列表框更改事件更新为如下所示(基本上只是将列表框条目的设置外包给新函数):

  1. Private Sub lbox_QuestionList_Change()
  2. ' 确保此事件在以编程方式更改时不会不断触发
  3. If Not EventsOn Then Exit Sub
  4. '
  5. If Not QuestionSaved(CurrentQuestion) Or aAnswers(CurrentQuestion) <> Me.tb_Question.Text Then ' 我添加了第二个条件用于测试目的,可能在您的完整代码中不必要
  6. If MsgBox(Prompt:="放弃对当前问题的更改吗?", Title:="当前问题未保存", Buttons:=vbYesNo + vbDefaultButton2) = vbYes Then
  7. ' 放弃更改 =
  8. ' 标记为已保存
  9. QuestionSaved(CurrentQuestion) = True
  10. ' 然后正常进行更改
  11. ' (如果用户返回到此问题,它将以其原始形式从内存中重新加载)
  12. ' 这个方法可以正常工作
  13. Else
  14. ' 放弃更改 = 否
  15. EventsOn = False ' 因此不会再次调用此子程序
  16. ' 恢复ListBox选择。通过重新调用当前问题编号并将其应用于ListIndex来执行此操作
  17. SelectedListIndex = CurrentQuestion - 1 ' 请记住索引将减去1,因为索引从0开始
  18. PopulateListbox SelectedListIndex ' 在此处调用您的新函数
  19. EventsOn = True
  20. Exit Sub ' 这应该是它的结尾但不知何故它不是...
  21. End If
  22. End If
  23. ' 继续根据新选择的ListIndex加载新问题
  24. SelectedListIndex = lbox_QuestionList.ListIndex ' 识别当前选择
  25. ' ListIndex从零开始,所以我们需要加1
  26. CurrentQuestion = SelectedListIndex + 1
  27. ShowQuestion CurrentQuestion
  28. End Sub
英文:

After looking into it for awhile, it appears that having the listbox set its own listindex from within its own change event (effectively recursively calling it) causes some weird backend issues. Fortunately, it's easy enough to deal with by migrating that bit out to its own function. After some experimenting, the best way to do it would be to create a function that clears and repopulates the listbox, so create this function in your UserForm code:

  1. Private Function PopulateListbox(Optional ByVal arg_lSelected As Long = -1)
  2. Me.lbox_QuestionList.Clear
  3. Dim X As Long &#39;
  4. For X = 1 To NumberofQuestions
  5. lbox_QuestionList.AddItem &quot;Question &quot; &amp; X &#39; Populate the listbox with items
  6. QuestionSaved(X) = True &#39; Flag the initial state as saved, for each question
  7. &#39;If Not X = rst.RecordCount Then rst.MoveNext
  8. Next X
  9. Me.lbox_QuestionList.ListIndex = arg_lSelected
  10. End Function

Now adjust your Initialize event to look like this (note that you need to define NumberofQuestions here, and then call the new function at the end to populate the listbox and select the first entry):

  1. Private Sub UserForm_Initialize()
  2. &#39; Stripped out lots of code here. Basically opens a recordset and loads values
  3. NumberofQuestions = 3 &#39;This is where NumberofQuestions gets defined
  4. ReDim QuestionSaved(1 To NumberofQuestions)
  5. ReDim aAnswers(1 To NumberofQuestions)
  6. &#39;
  7. &#39; Select the first question by default. Note that the Listbox ListIndex starts at 0, whereas the questions start at 1
  8. SelectedListIndex = 0
  9. CurrentQuestion = 1
  10. EventsOn = True
  11. PopulateListbox SelectedListIndex &#39;Call the new function and set the 1st selection
  12. End Sub

Lastly, update your listbox_change event to look like this (basically just outsourcing the setting of the listbox entry to the new function):

  1. Private Sub lbox_QuestionList_Change()
  2. &#39; Ensure this event does NOT keep firing in a loop, when changed programmatically
  3. If Not EventsOn Then Exit Sub
  4. &#39;
  5. If Not QuestionSaved(CurrentQuestion) Or aAnswers(CurrentQuestion) &lt;&gt; Me.tb_Question.Text Then &#39;I added the second condition for testing purposes, may not be necessary in your full code
  6. If MsgBox(Prompt:=&quot;Abandon changes to current question?&quot;, Title:=&quot;Current question not saved&quot;, Buttons:=vbYesNo + vbDefaultButton2) = vbYes Then
  7. &#39; Abandon changes = Yes
  8. &#39; Mark as saved
  9. QuestionSaved(CurrentQuestion) = True
  10. &#39; Then proceed to change as normal
  11. &#39; (If the user comes back to this question, it will be re-loaded from memory in its original form)
  12. &#39; This works okay
  13. Else
  14. &#39; Abandon changes = No
  15. EventsOn = False &#39; So this sub is not called again
  16. &#39; REVERT the ListBox selection. Do this by recalling the current question number, and applying it to the ListIndex
  17. SelectedListIndex = CurrentQuestion - 1 &#39; Remember that the index will be minus 1, due to indexing starting at 0
  18. PopulateListbox SelectedListIndex &#39;Call your new function here
  19. EventsOn = True
  20. Exit Sub &#39; This should be the end of it. But somehow, it&#39;s not...
  21. End If
  22. End If
  23. &#39; Proceed with loading a new question according to the new selected ListIndex
  24. SelectedListIndex = lbox_QuestionList.ListIndex &#39; Recognise the current selection
  25. &#39; ListIndex starts at zero, so we need to add 1
  26. CurrentQuestion = SelectedListIndex + 1
  27. ShowQuestion CurrentQuestion
  28. End Sub

答案2

得分: 2

以下是您提供的代码的翻译部分:

  1. *(谴责OP让这个问题困扰我的脑子)*
  2. 在我的测试中我使用了以下UserForm
  3. [![输入图像描述][1]][1]
  4. 下面的代码使用了`ListBox1_AfterUpdate`事件我相信它可能适用于您
  5. ```vb
  6. Option Explicit
  7. Private Const TOTAL_QUESTIONS As Long = 3
  8. Private qSaved As Variant
  9. Private selectedDuringTextboxChange As Long
  10. Private eventsInProgress As Long
  11. Private Sub ListBox1_AfterUpdate()
  12. Debug.Print "列表框被点击,项目 " & (ListItemSelected() + 1) & " 被选中"
  13. If eventsInProgress > 0 Then
  14. Debug.Print " ... 事件正在进行中,退出"
  15. eventsInProgress = eventsInProgress - 1
  16. Exit Sub
  17. End If
  18. If Not qSaved(selectedDuringTextboxChange) Then
  19. Dim answer As VbMsgBoxResult
  20. answer = MsgBox("放弃更改吗?", vbYesNo + vbDefaultButton2)
  21. If answer = vbYes Then
  22. Debug.Print "是的,放弃更改"
  23. qSaved(selectedDuringTextboxChange) = True
  24. Else
  25. Debug.Print "不,保留更改"
  26. '--- 返回到之前选择的列表项
  27. eventsInProgress = eventsInProgress + 1
  28. UnselectAll
  29. ListBox1.Selected(selectedDuringTextboxChange - 1) = True
  30. ListBox1.ListIndex = selectedDuringTextboxChange - 1
  31. End If
  32. End If
  33. End Sub
  34. Private Sub QuitButton_Click()
  35. Me.Hide
  36. End Sub
  37. Private Sub SaveButton_Click()
  38. qSaved(ListBox1.ListIndex + 1) = True
  39. End Sub
  40. Private Sub TextBox1_Change()
  41. selectedDuringTextboxChange = ListBox1.ListIndex + 1
  42. qSaved(selectedDuringTextboxChange) = False
  43. Debug.Print "更改了问题 " & selectedDuringTextboxChange & " 的文本"
  44. End Sub
  45. Private Sub UserForm_Initialize()
  46. ReDim qSaved(1 To TOTAL_QUESTIONS)
  47. selectedDuringTextboxChange = 1
  48. With ListBox1
  49. Dim i As Long
  50. For i = 1 To TOTAL_QUESTIONS
  51. .AddItem "问题 " & i
  52. qSaved(i) = True
  53. Next i
  54. .Selected(0) = True
  55. End With
  56. eventsInProgress = False
  57. End Sub
  58. Private Sub UnselectAll()
  59. eventsInProgress = eventsInProgress + 1
  60. With ListBox1
  61. Dim i As Long
  62. For i = 0 To .ListCount - 1
  63. .Selected(i) = False
  64. Next i
  65. End With
  66. eventsInProgress = eventsInProgress - 1
  67. End Sub
  68. Private Function ListItemSelected() As Long
  69. ListItemSelected = -1
  70. With ListBox1
  71. Dim i As Long
  72. For i = 0 To .ListCount - 1
  73. If .Selected(i) Then
  74. ListItemSelected = i
  75. End If
  76. Next i
  77. End With
  78. End Function
  79. Private Sub WhichListItem_Click()
  80. With ListBox1
  81. Dim i As Long
  82. For i = 0 To .ListCount - 1
  83. Debug.Print "列表框项目(" & i & ") = " & .Selected(i)
  84. Next i
  85. End With
  86. Debug.Print "eventsInProgress = " & eventsInProgress
  87. End Sub
  1. <details>
  2. <summary>英文:</summary>
  3. *(curses to OP for getting this problem in my brain!)*
  4. In my testing, I used the following UserForm:
  5. [![enter image description here][1]][1]
  6. The code below uses the `ListBox1_AfterUpdate` event, and I believe it may work for you.
  7. Option Explicit
  8. Private Const TOTAL_QUESTIONS As Long = 3
  9. Private qSaved As Variant
  10. Private selectedDuringTextboxChange As Long
  11. Private eventsInProgress As Long
  12. Private Sub ListBox1_AfterUpdate()
  13. Debug.Print &quot;listbox clicked, item &quot; &amp; (ListItemSelected() + 1) &amp; &quot; selected&quot;
  14. If eventsInProgress &gt; 0 Then
  15. Debug.Print &quot; ... event in progress, exiting&quot;
  16. eventsInProgress = eventsInProgress - 1
  17. Exit Sub
  18. End If
  19. If Not qSaved(selectedDuringTextboxChange) Then
  20. Dim answer As VbMsgBoxResult
  21. answer = MsgBox(&quot;Abandon changes?&quot;, vbYesNo + vbDefaultButton2)
  22. If answer = vbYes Then
  23. Debug.Print &quot;yes, abandon the changes&quot;
  24. qSaved(selectedDuringTextboxChange) = True
  25. Else
  26. Debug.Print &quot;nope, keep the changes&quot;
  27. &#39;--- return to the previously selected list item
  28. eventsInProgress = eventsInProgress + 1
  29. UnselectAll
  30. ListBox1.Selected(selectedDuringTextboxChange - 1) = True
  31. ListBox1.ListIndex = selectedDuringTextboxChange - 1
  32. End If
  33. End If
  34. End Sub
  35. Private Sub QuitButton_Click()
  36. Me.Hide
  37. End Sub
  38. Private Sub SaveButton_Click()
  39. qSaved(ListBox1.ListIndex + 1) = True
  40. End Sub
  41. Private Sub TextBox1_Change()
  42. selectedDuringTextboxChange = ListBox1.ListIndex + 1
  43. qSaved(selectedDuringTextboxChange) = False
  44. Debug.Print &quot;changed text for question &quot; &amp; selectedDuringTextboxChange
  45. End Sub
  46. Private Sub UserForm_Initialize()
  47. ReDim qSaved(1 To TOTAL_QUESTIONS)
  48. selectedDuringTextboxChange = 1
  49. With ListBox1
  50. Dim i As Long
  51. For i = 1 To TOTAL_QUESTIONS
  52. .AddItem &quot;Question &quot; &amp; i
  53. qSaved(i) = True
  54. Next i
  55. .Selected(0) = True
  56. End With
  57. eventsInProgress = False
  58. End Sub
  59. Private Sub UnselectAll()
  60. eventsInProgress = eventsInProgress + 1
  61. With ListBox1
  62. Dim i As Long
  63. For i = 0 To .ListCount - 1
  64. .Selected(i) = False
  65. Next i
  66. End With
  67. eventsInProgress = eventsInProgress - 1
  68. End Sub
  69. Private Function ListItemSelected() As Long
  70. ListItemSelected = -1
  71. With ListBox1
  72. Dim i As Long
  73. For i = 0 To .ListCount - 1
  74. If .Selected(i) Then
  75. ListItemSelected = i
  76. End If
  77. Next i
  78. End With
  79. End Function
  80. Private Sub WhichListItem_Click()
  81. With ListBox1
  82. Dim i As Long
  83. For i = 0 To .ListCount - 1
  84. Debug.Print &quot;listbox item(&quot; &amp; i &amp; &quot;) = &quot; &amp; .Selected(i)
  85. Next i
  86. End With
  87. Debug.Print &quot;eventsInProgress = &quot; &amp; eventsInProgress
  88. End Sub
  89. [1]: https://i.stack.imgur.com/pXyh2.png
  90. </details>
  91. # 答案3
  92. **得分**: 0
  93. Private Sub ListBox_Click() 函数运行两次的问题。
  94. 当我在列表框属性中清除了 ControlSource 后,问题就解决了。我不得不添加一行代码,以明确将列表框中的值写入工作表中的单元格。起初,单元格不会显示数据,所以我将范围名称设置为另一个单元格,那就没问题了。然后,我将新单元格拖放到原始位置。
  95. 我不明白问题从何而来,但修复方法有效。
  96. <details>
  97. <summary>英文:</summary>
  98. I had a problem with a **Private Sub ListBox_Click()** running twice.
  99. When I cleared the *ControlSource* in the list box properties it fixed the problem. I had to add a line of code to specifically write the value from the *listbox* to the cell in the worksheet. At first the cell would not display the data so I set the range name to another cell and that was OK. So, I then dragged and dropped the new cell into the original location.
  100. I don&#39;t understand where the problem originated, but the fix worked.
  101. </details>
  102. # 答案4
  103. **得分**: 0
  104. I had a similar unexpected issue, so maybe someone will find this result helpful.
  105. Within a multi-selection-enabled Listbox_Change event, I checked the value of the currently-selected item to see whether it had been checked or unchecked.
  106. ``` Private Sub lstBox_Change()
  107. With lstBox
  108. If .Selected(.ListIndex) Then
  109. ' Call Method A.
  110. Else
  111. ' Call Method B.
  112. End If
  113. End With
  114. End Sub

当列表被选中时,它会正确检测到选择并调用 A,但是,当代码执行到 Change 事件的 End Sub 时,复选框会自动取消选择,并再次触发 Change 事件。请注意,我没有在 ListBox 中设置任何值;我只是检查当前项目是否被选中或取消选中。但不知何故,这会触发它自动取消选择。 (此外,这似乎只发生在对 Change 事件的第一次调用上。之后它表现正常。)

我尝试了一些其他修复方法,但 BeforeUpdateAfterUpdate 似乎根本不会触发。当我将选择测试移出 If 语句并将结果放入布尔变量中时,问题消失了:

  1. With lstBox
  2. BooleanResult = (.Selected(.ListIndex) = True)
  3. If BooleanResult Then
  4. ' Call Method A.
  5. Else
  6. ' Call Method B.
  7. End If
  8. End With
  9. End Sub

之后,ListBox 一直按预期行为。

英文:

I had a similar unexpected issue, so maybe someone will find this result helpful.
Within a multi-selection-enabled Listbox_Change event, I checked the value of the currently-selected item to see whether it had been checked or unchecked.

  1. Private Sub lstBox_Change()
  2. With lstBox
  3. If .Selected(.ListIndex) Then
  4. &#39; Call Method A.
  5. Else
  6. &#39; Call Method B.
  7. End If
  8. End With
  9. End Sub

When the list was checked, it would properly detect the selection and call A--but then, when stepping through the code and reaching the Change event's End Sub, the checkbox would automatically become unselected, and the Change event would fire again. Note that I wasn't setting any value in the ListBox itself; I was only checking to see whether the current item was checked or unchecked. But, somehow, that triggered it to unselect itself. (Also, this only seemed to happen on the first call to the Change event. Thereafter it behaved normally.)</p>
I tried some of the other fixes, but BeforeUpdate and AfterUpdate never seemed to fire at all. The problem went away when I moved the selection test outside of the If statement and put the result into a Boolean instead:

  1. Private Sub lstBox_Change()
  2. With lstBox
  3. BooleanResult = (.Selected(.ListIndex) = True)
  4. If BooleanResult Then
  5. &#39; Call Method A.
  6. Else
  7. &#39; Call Method B.
  8. End If
  9. End With
  10. End Sub

After that, the ListBox consistently behaved as expected.

huangapple
  • 本文由 发表于 2020年1月3日 23:15:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/59581009.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定