如何在 Excel 的 VBA 中有效地动态创建控件或如何使用 Application.OnTime()?
How do I effectively create controls dynamically in Excel's VBA or How do I use Application.OnTime()?
我的工作是 Excel 中的一个非常大的 VBA 项目。我们仅针对一个功能就编写了大约 1500 行代码,并且还有大约十几个要添加的功能。因此,我一直在尝试分解所有内容,以便将每个功能的代码保存在不同的位置。 OOP 很糟糕 VBA... 问题是这些控件必须触发事件。当然,有些事件(如 TextBox_AfterUpdate 事件)在动态创建控件时不可用。由于正在发生的一切,它有点令人费解,所以我会尽我所能分解它:
我有一个 class 模块,代表多页控件的选项卡。当用户单击选项卡时,用户窗体调用此 class 模块,我在那里动态创建了控件。这样我就可以将代码保留在 class 模块中。我有一个我认为是 "AfterUpdate" 子项的子项,并将我需要的代码放在 运行 那里。现在的问题是在适当的时间调用该子程序。
所以我所做的是设置一个定时器来检查 "ActiveControl" 是否是文本框。如果不是,我们可以假设焦点已经离开,我们可以引发该事件。这是我正在使用的代码:
选项卡创建的简化版本...
Private WithEvents cmbMarketplace As MSForms.ComboBox
Public Sub LoadTab(ByVal oPageTab As Object)
If TabLoaded Then Exit Sub
Set PageTab = oPageTab
Dim tmp As Object
Set tmp = PageTab.Add("Forms.Label.1")
tmp.Top = 6: tmp.Left = 6: tmp.Width = 48
tmp.Caption = "Marketplace:"
Set cmbMarketplace = PageTab.Add("Forms.ComboBox.1", "cmbMarketplace")
' LOAD OTHER CONTROLS '
TabLoaded = True
Start_Timer
End Sub
然后Start_Timer:
Public Sub Start_Timer()
TimerActive = True
Application.OnTime Now() + TimeValue("00:00:01"), "Timer"
End Sub
以及要发射的潜艇:
Public Sub Timer()
If TimerActive Then
' DO SOME RANDOM THINGS '
Application.OnTime Now() + TimeValue("00:00:01"), "Timer"
End If
End Sub
这看起来是解决我面临的问题的合理方法吗?我乐于接受建议...
这是第一个问题。这似乎需要做很多工作才能完成。 (我正在努力获得 visual studio,但我不知道这是否会发生)
上面的代码可以工作,但是 "Timer" sub 根本不会被引发。如果我只是 运行 代码,我不会出错。一切都被创造了,一切都如我所愿。但是,如果我单步执行代码,最终会出现以下错误:
Cannot run the macro "...xlsm!Timer". The macro may not be available in this workbook or all macros may be disabled.
显然这些建议都无效。宏已启用,子模块位于同一个 class 模块中。我试过 public,同样的问题。尝试 "ClassModule1!Timer" 无济于事。我无能为力试图弄清楚这一点。考虑让人们在用户表单中写下所有这些或者干脆放弃。
有人对如何有效分解大块代码有什么建议吗?有没有人知道为什么这个潜艇不会 运行 并且似乎找不到?
我知道这是一个令人困惑的情况,所以如果您需要更多信息或代码示例,或者想知道为什么我按照我的方式设置某些东西,请告诉我。
谢谢!
Obviously neither of those suggestions are valid. Macros ARE enabled and the sub is in the same darn class module.
有问题:宏不能在class模块中。该消息完全正确:VBA 看不到 Timer
过程,因为它不可访问。
A class 模块是 object 的蓝图,VBA(或与此相关的任何 OOP 语言)不能对class 模块,没有那个 class 的 实例 - 即一个对象。
您的定时器回调需要是标准模块中的Public Sub
,以便可以直接作为宏调用[=32] =]. Public class 模块的过程是 方法 ,而不是 宏 。
根据 ' DO SOME RANDOM THINGS '
的实际含义,这可能需要也可能不需要进行一些重组。
1500 行意大利面条代码可以用任何语言编写顺便说一句。
我的工作是 Excel 中的一个非常大的 VBA 项目。我们仅针对一个功能就编写了大约 1500 行代码,并且还有大约十几个要添加的功能。因此,我一直在尝试分解所有内容,以便将每个功能的代码保存在不同的位置。 OOP 很糟糕 VBA... 问题是这些控件必须触发事件。当然,有些事件(如 TextBox_AfterUpdate 事件)在动态创建控件时不可用。由于正在发生的一切,它有点令人费解,所以我会尽我所能分解它:
我有一个 class 模块,代表多页控件的选项卡。当用户单击选项卡时,用户窗体调用此 class 模块,我在那里动态创建了控件。这样我就可以将代码保留在 class 模块中。我有一个我认为是 "AfterUpdate" 子项的子项,并将我需要的代码放在 运行 那里。现在的问题是在适当的时间调用该子程序。
所以我所做的是设置一个定时器来检查 "ActiveControl" 是否是文本框。如果不是,我们可以假设焦点已经离开,我们可以引发该事件。这是我正在使用的代码:
选项卡创建的简化版本...
Private WithEvents cmbMarketplace As MSForms.ComboBox
Public Sub LoadTab(ByVal oPageTab As Object)
If TabLoaded Then Exit Sub
Set PageTab = oPageTab
Dim tmp As Object
Set tmp = PageTab.Add("Forms.Label.1")
tmp.Top = 6: tmp.Left = 6: tmp.Width = 48
tmp.Caption = "Marketplace:"
Set cmbMarketplace = PageTab.Add("Forms.ComboBox.1", "cmbMarketplace")
' LOAD OTHER CONTROLS '
TabLoaded = True
Start_Timer
End Sub
然后Start_Timer:
Public Sub Start_Timer()
TimerActive = True
Application.OnTime Now() + TimeValue("00:00:01"), "Timer"
End Sub
以及要发射的潜艇:
Public Sub Timer()
If TimerActive Then
' DO SOME RANDOM THINGS '
Application.OnTime Now() + TimeValue("00:00:01"), "Timer"
End If
End Sub
这看起来是解决我面临的问题的合理方法吗?我乐于接受建议...
这是第一个问题。这似乎需要做很多工作才能完成。 (我正在努力获得 visual studio,但我不知道这是否会发生)
上面的代码可以工作,但是 "Timer" sub 根本不会被引发。如果我只是 运行 代码,我不会出错。一切都被创造了,一切都如我所愿。但是,如果我单步执行代码,最终会出现以下错误:
Cannot run the macro "...xlsm!Timer". The macro may not be available in this workbook or all macros may be disabled.
显然这些建议都无效。宏已启用,子模块位于同一个 class 模块中。我试过 public,同样的问题。尝试 "ClassModule1!Timer" 无济于事。我无能为力试图弄清楚这一点。考虑让人们在用户表单中写下所有这些或者干脆放弃。
有人对如何有效分解大块代码有什么建议吗?有没有人知道为什么这个潜艇不会 运行 并且似乎找不到?
我知道这是一个令人困惑的情况,所以如果您需要更多信息或代码示例,或者想知道为什么我按照我的方式设置某些东西,请告诉我。
谢谢!
Obviously neither of those suggestions are valid. Macros ARE enabled and the sub is in the same darn class module.
有问题:宏不能在class模块中。该消息完全正确:VBA 看不到 Timer
过程,因为它不可访问。
A class 模块是 object 的蓝图,VBA(或与此相关的任何 OOP 语言)不能对class 模块,没有那个 class 的 实例 - 即一个对象。
您的定时器回调需要是标准模块中的Public Sub
,以便可以直接作为宏调用[=32] =]. Public class 模块的过程是 方法 ,而不是 宏 。
根据 ' DO SOME RANDOM THINGS '
的实际含义,这可能需要也可能不需要进行一些重组。
1500 行意大利面条代码可以用任何语言编写顺便说一句。