【问题标题】:Changing sheet codename Run-Time Error 9: Subscript out of range更改工作表代号运行时错误 9:下标超出范围
【发布时间】:2017-04-22 16:23:55
【问题描述】:

我的代码在我按下按钮时会向工作簿添加一个新工作表并更改工作表的代号,以便稍后在我的代码中引用。

Dim wbk As Workbook
Dim wks As Worksheet

Set wbk = ThisWorkbook

wbk.Sheets.Add.Name = "Admin - Save Log"
Set wks = wbk.Worksheets("Admin - Save Log")
wks.Parent.VBProject.VBComponents(wks.CodeName).Name = "wksAdminSaveLog"

这确实有效 - 但是 - 只有当我打开“开发者”窗口或之前已经打开它时。

如果我在第一次打开 Excel 文档(尚未打开“开发人员”窗口)时单击按钮,它会添加工作表,但是会出现以下错误并且不会更改工作表的代号:

运行时错误“9”:下标超出范围

只有当我按下调试,然后在“开发人员”窗口再次打开后运行代码时,它才会添加代号。

有什么办法可以让用户不必打开开发者窗口就可以正常运行?

【问题讨论】:

  • 你应该Set wks = wbk.Sheets.Add然后设置它的.Name。这样您就不需要将字符串文字拼写两次。
  • 您可以尝试一两个DoEvents 电话。但是,如果可以的话,使用Application.Ontime 调用来运行代码以更改代号(以及发生的任何其他事情)可能会更安全。
  • 澄清一下,wks.Parent.VBProject.VBComponents(wks.CodeName) 访问是否引发了运行时错误 9?
  • 您在当前正在执行的 VBProject 中添加和重命名 VBComponents。你很幸运,整个 IDE 和主机应用程序还没有完全崩溃——我怀疑 VBE 需要专门加载,以便涉及 P-Code 编译器或 VBA 解释器的幕后东西有机会选择启动新组件;如果 VBE 尚未加载,则您正在执行已编译的代码,其中 wks.CodeName 不作为组件存在。棘手的东西。有趣的问题。
  • 尽管我仍然想知道这个特定问题的答案 - 是否有理由你不能只在工作簿中使用正确的代号但将可见性设置为 @ 987654328@什么时候不需要?

标签: excel vba runtime-error rename worksheet


【解决方案1】:

如果您需要打开 VBE,您可以“欺骗”调试上下文为您执行此操作。这似乎可以通过强制 VBE.MainWindow 存在来完成项目需要更新其索引的任何事情:

Dim wbk As Workbook
Dim wks As Worksheet

Set wbk = ThisWorkbook

Set wks = wbk.Sheets.Add
wks.Name = "Admin - Save Log"
Debug.Print wbk.VBProject.VBE.MainWindow.Caption   '<--Force the VBE to exist.
wbk.VBProject.VBComponents(wks.CodeName).Name = "wksAdminSaveLog"

编辑:

似乎只需获取对VBE.MainWindow 的引用就足够了(请参阅 cmets)。这也有效:

Dim editor As Object
Set editor = wbk.VBProject.VBE.MainWindow   '<--Force the VBE to exist.

【讨论】:

  • 仅仅引用wbk.VBProject.VBE.MainWindow.Caption 就足够了,还是必须明确地与 Debug 对象一起使用? (我知道Debug.Print 更适合“丢弃”代码 - 只是好奇)
  • @MacroMan - 不完全确定。我只测试了Debug.Print,但没有做任何事情。打印Caption 是我想到的第一件事,它会得到一个一直到MainWindow 的属性请求。设置对象引用似乎也有效。我会更新答案。
【解决方案2】:

@Comintern 已经为您提供了一个可行的解决方案,但此代码不会污染您的即时窗口,并使用隐藏的 _CodeName 属性来更改工作表名称,而不是访问 vbComponents 集合。

它还对wks 使用早期绑定的Worksheet 赋值,然后使用With 块,因为它正在访问wks 的多个成员。

有趣的是,VBProject 成员使用的位置很重要。

Dim wbk As Workbook
Dim wks As Worksheet

Set wbk = ThisWorkbook
Set wks = wbk.Worksheets.Add

'Oddly, this statement MUST appear AFTER the Worksheets.add
Debug.Assert wbk.VBProject.Name <> vbNullString 'Don't pollute the Immediate window

With wks
  .Name = "Admin - Save Log"
  .[_CodeName] = "wksAdminSaveLog"
End With

【讨论】:

  • 我认为这一点也不奇怪:您在 项目中存在所有 VBComponents 之后“创建”了 VBE。
  • @Mat'sMug - 实际上这根本不会取消对 VBE 的引用。我想知道我是否正在为栅栏而摇摆,并且本可以在取消引用wbk.Project 时停下来。 @ThunderFrame - 取消引用项目后是否必须使用隐藏的 _CodeName?顺便说一句,.Assert 很好。
  • @Comintern 嗯,我的意思是“调试环境”中的“VBE”,或者任何使 VBA 运行时了解正在发生的事情的东西。对[_CodeName] 的良好调用,IIRC 不使用该隐藏属性是 Rubberduck 的 refactor/rename 功能在一次重命名工作表时的错误。
  • 我没有尝试香草CodeName 因为我知道 Rubberduck 的作用。在我意识到 vbProject 的位置很重要之前,我认为我发现了在 with 块中使用 `[_CodeName] 的错误。
  • @ThunderFrame 当我尝试这种方法时,它会在 Debug.Assert 位上出现以下错误。 . . . .运行时错误 '1004:对 Visual Basic 项目的编程访问不受信任。 . . .关于如何解决这个问题的任何想法?
【解决方案3】:

另一种强制刷新 VBComponents 集合的简单方法:

PropertyGetDiscard ThisWorkbook.VBProject.VBComponents.Count

 

Private Sub PropertyGetDiscard(AnyPropertyGet): End Sub

过程PropertyGetDiscard用于避免污染立即窗口或使用多余的变量

【讨论】:

    【解决方案4】:

    解决这种依赖于 VBComponent 的代码问题的一般方法是使用 VbeInit procedure given here,它可以灵活地为每个新打开的工作簿调用多次尚未被“VBComponent-初始化”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多