VBA:在宏运行时禁用列表框

4

问题

我有一个宏(我将其称为launch_macro),通过在用户窗体ListBox(ListBox1_DblClick)上双击启动。

我的问题是,如果用户在宏仍在运行时再次双击,那么无论我是否禁用ListBox,一旦第一次执行完成,宏都会被再次启动。

代码和测试

Private sub ListBox1_DblClick(Byval Cancel as MSForms.ReturnBoolean)

   (....Logging...)

    If Not Cancel Then
        Me.ListBox1.Enabled = False
        (...DisplayStatusBar / ScreenUpdating / ListBox1.BackColor...)
        launch_macro
        (...DisplayStatusBar / ScreenUpdating / ListBox1.BackColor...)
        Me.ListBox1.Enabled = True

    End If

End sub

看起来Excel在关联的ListBox被禁用时记录/排队 ListBox1_DblClick 事件(以便将来执行)。为什么会这样?我该如何防止这种情况发生?
我也尝试了以下方法,但都没有成功:
- Locked Me.ListBox1.Locked = True - Doevents :在 Me.ListBox1.Enabled = False 之后添加 DoEvents - EnableEvents Application.EnableEvents = False - macroLaunched variable :使用变量检查宏是否已启动(在 ListBox1_DblClick 事件的开始处设置 macroLaunched = True ,在结束时设置 macroLaunched = False )。由于第二次执行是在第一个事件结束之后启动的(因此变量被设置回 False ),因此这不起作用。 (而在Dbl_Click事件范围之外将变量设置回False是不可接受的,因为用户需要能够立即再次启动宏(但不能在第一次执行仍在运行时)。 - 添加延迟 (仅用于测试):我在启动macro后立即添加了10秒的延迟(Application.Wait)。然后我在1秒内双击了两次。第二次执行仍然启动。我通过日志记录:第二个ListBox1.Dbl_Click事件在第一个事件后12秒被“记录”到Excel中。 注意:我使用的是Office Standard 2013

当前“解决方案”

这个技巧是从A.S.H答案中改编的(以减少延迟)。
Private sub ListBox1_DblClick(Byval Cancel as MSForms.ReturnBoolean)
   Static nextTime As Single

   If Timer < nextTime then
        Log_macro "Event canceled because Timer < nextTime : " & Timer
        Exit Sub
   End if

   (....Logging...)

    If Not Cancel Then
        (...DisplayStatusBar / ScreenUpdating / ListBox1.BackColor...)
        launch_macro
        (...DisplayStatusBar / ScreenUpdating / ListBox1.BackColor...)

    End If

    nextTime = Timer + 0.5
    Log_macro "nextTime = " & nextTime

End sub

这个方法“可以解决问题”,但是我仍然不喜欢 ListBox1 仍然处于启用状态,而且 Excel 仍在排队事件,因此我需要估计用户可能会 Dbl_Click 的次数(取决于宏花费的时间),以估算我需要多少延迟(目前为0.5秒,以便能够处理(并记录)至少10个取消事件)。另外,似乎 Excel 在运行宏时不太喜欢(性能方面)排队事件。


@YowE3K。刚刚测试了EnableEvents;它也不起作用。 :-( - Tibo
是的 - 我意识到这是在一个实际的表格上,所以决定那可能不是正确的答案,于是删除了评论。 - YowE3K
2
在启动宏之前,请检查macroLaunched是否为true。如果是,则不要启动宏。如果不是,则在启动宏之前将其设置为true。在doubleClick事件之外设置macroLaunched,当可以再次启动宏时。 - Rich Holton
我再次检查了一遍,宏中没有任何 DoEvents。记录日志的速度太快而不可能是问题所在,即使在开头添加 Me.ListBox1.Enabled = False 仍然存在问题。我不明白为什么只有我会出现这个问题…… - Tibo
1
@Tibo,也许我没有表达清楚我的想法。请尝试下面我回答中的方法。 - A.S.H
显示剩余12条评论
2个回答

3

我会提出我的建议,希望你尝试一下,因为可能被误解了。这个想法是,一旦宏完成,我们在再次处理双击事件之前设置n秒的延迟(比如2秒)。这样,在这两秒钟内排队等待执行的双击将被处理,但不会产生任何效果。

Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
    Static NextTime As Variant ' Will set a barrier for launching again the macro 
    If Not IsEmpty(NextTime) Then If Now < NextTime Then Exit Sub

    ListBox1.Enabled = False
    ' Any event code
    launch_macro
    ' ...
    ListBox1.Enabled = True

    NextTime = Now + TimeSerial(0, 0, 2) ' dbl-click events will have no effects during next 2 seconds
End Sub

1
如果您将NextTime定义为Date,则无需检查IsEmpty,它将被初始化为第0天,因此Now不会小于其初始值。 - YowE3K
@YowE3K 发现得好。更改留给 OP 自己决定,我个人更喜欢保持这种方式,因为我可以明确地看到它,但这只是个人选择 :) - A.S.H
1
@A.S.H 谢谢。使用静态变量的方法可能会奏效,但是使用 Static NextTime As Single 并且使用 Timer 而不是 Now,甚至可以让我使用仅为 0.1 秒的延迟,并且这使得 ListBox1.Enabled 语句无用。由于现在已经快凌晨两点了(即 Const myNeededSleepTime As Single = 6*3600nextTime = Timer + myNeededSleepTime...),我将在明天进行测试。 - Tibo
1
@Tibo 祝你做个好梦 :) - A.S.H
你的建议(结合 nextTime As SingleTimer)“解决了问题”(有合理的延迟);谢谢。但我仍然不喜欢 ListBox1 仍然启用,Excel 仍在排队事件,因此我需要估计用户可能会 Dbl_Click 的次数(取决于宏花费的时间),以估计我需要多少延迟(目前为0.5秒,以便能够处理(和记录)至少10个取消事件)。此外,似乎 Excel 在运行宏时不太喜欢(性能方面)排队事件。 - Tibo
是的,我们必须承认这并不优雅。双击事件似乎与禁用控件无关...禁用涉及用户在控件内选择或输入内容的能力。这可能是有意为之的设计。 - A.S.H

0
你可以使用一个变量来锁定代码的关键部分一段时间。下面的示例锁定了Excel工作簿中Sheet2函数Test()的关键部分。
Option Explicit
Private booIsRunning As Boolean
Private Sub Test()

    If Not booIsRunning Then
        booIsRunning = True
        Debug.Print "Hello."
        Application.OnTime Now + TimeValue("00:00:02"), "Sheet2.UnlockTest"
    End If
End Sub

Public Sub UnlockTest()
    booIsRunning = False
End Sub

谢谢。这可能会起作用,但我不太愿意为这样一个简单的问题使用Application.OnTime和多个子程序。至于技巧,我更喜欢A.S.H的答案(我的当前解决方案)。 - Tibo
感谢您的反馈。如果这是一个用户表单,那么更好的方法可能在这里找到:http://www.cpearson.com/excel/SuppressChangeInForms.htm。 - steve_cdi
我刚才看了一下,它确实是一个“用户窗体”,但我非常确定它不会起作用,因为它不是一个“更改属性”引发不同事件,而是在执行时(ListBox1_DblClick)已经引发的相同事件(ListBox1_DblClick)。就像我关于“变量”方法(称其为macroLaunched或blockEvents)所述:“这不起作用,因为第二次执行是在第一个事件结束后启动的(因此变量被设置回False)。 - Tibo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接