【问题标题】:Userform makes Excel crash depending on conditionUserform 根据条件使 Excel 崩溃
【发布时间】:2018-03-29 13:01:10
【问题描述】:

我遇到了一个我不明白的有趣问题。

在 VBA 中,我想在用户双击单元格时加载用户窗体,具体取决于单元格内容。但是根据我问的情况,一旦我退出 Sub,Excel 就会崩溃(即使在 UserForm 关闭后它也会继续执行代码)。

这行得通:

Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
    Dim myForm As New UserForm1
    If Target.Cells(1).Value = "" Then
        myForm.Show
    End If
    Debug.Print ("OK")
End Sub

而这三个使 Excel 崩溃:

1.

Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
    Dim myForm As New UserForm1
    If Target.Cells(1).Value <> "" Then
        myForm.Show
    End If
    Debug.Print ("OK")
End Sub

2.

Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
    Dim myForm As New UserForm1
    If Not Target.Cells(1).Value = "" Then
        myForm.Show
    End If
    Debug.Print ("OK")
End Sub

3.

Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
    Dim myForm As New UserForm1
    If Target.Cells(1).Value = "Test" Then
        myForm.Show
    End If
    Debug.Print ("OK")
End Sub

我正在使用一个空的用户窗体进行测试

【问题讨论】:

标签: vba excel userform


【解决方案1】:

尝试更改 If 块中的代码,使其执行 Cancel = True 以忽略双击操作并预先加载表单,如下所示:

If Target.Cells(1).Value = "Test" Then
    Cancel = True
    Load myForm
    myForm.Show
End If

【讨论】:

  • Load myForm 将是多余的,因为 myForm 是一个对象变量,会自动实例化。
  • ...我不确定这会对 OP 的计算机产生什么影响,但不是其他两个测试相同代码的人?
【解决方案2】:

Cancelling the cell edit mode entry 似乎是一个很好的方法来限制可能出错的事情的数量。

还有更多。

Dim myForm As New UserForm1

这是在本地范围内创建一个 自动实例化 UserForm1 对象。由于Dim 语句不可执行,这意味着无论您是否Show 表单,只要进入过程范围,就会创建对象。

使用With 块,您可以轻松避免无条件创建该对象,甚至无需为其声明变量:

Private Sub Workbook_SheetBeforeDoubleClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean)
    If Target.Cells(1).Value = "Test" Then
        Cancel = True
        With New UserForm1
            .Show
        End With
    End If
    Debug.Print "OK"
End Sub

这样,表单实例只有在需要时才会被创建。避免默认实例陷阱的巨大荣誉 - 你可以阅读更多关于on my blog的信息。

请注意,我还删除了 Debug.Print 参数列表周围的多余括号。括号在过程调用中实际上是有害的;当您将它们与多参数过程一起使用时,您会遇到编译错误,当您需要传递对象引用时会遇到运行时错误,以及当您打算传递参数时会遇到逻辑错误ByRef - 像这样使用的括号,正在强制 VBA 将表达式 作为值 进行计算,并始终传递结果 ByVal,而不管被调用的过程是否表示它正在使用 ByRef 参数。

在调用 函数 并在局部变量中捕获其返回值时使用括号。

【讨论】:

  • 我不知道我可以在 With 块中直接实例化和创建 Object。从现在开始,我会在适用时尝试使用它。感谢您提供有关使用括号的详细信息。
  • @Svatik 或者,您可以通过声明局部变量 Dim myForm As UserForm1,然后在条件块内执行 Set myForm = New UserForm1 来分隔 DimNew。不过,我喜欢With New 如何保存对象实例并确保其正确销毁(永远不要跳入/跳出With 块!)
  • 至于As New 的含义,请尝试执行Dim foo As New UserForm1,然后执行Set foo = Nothing,然后执行Debug.Print foo Is Nothing - 比较预期与实际;-)
  • 我不确定是否理解最后一部分。这是否意味着即使我对它没有任何影响,该对象也不会被破坏?还是当我在 Debug.Print 中使用它时再次实例化它?
  • 当您执行Dim foo As New Something 时,VBA 确保foo 在其声明的整个范围内都是Something 的有效实例,无论如何。在UserForm 的情况下,这可能意味着用户单击红色“X”以关闭表单,对象连同其状态一起被销毁(假设您没有处理QueryClose),然后当调用代码查询时表单的状态(例如某些属性或复选框状态),它正在读取的内容是默认状态,因为它不是在查看同一个对象,而是在现场重新生成的对象:VBA 根据需要重新创建对象。
猜你喜欢
  • 2021-10-10
  • 1970-01-01
  • 1970-01-01
  • 2016-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-30
  • 2016-07-29
相关资源
最近更新 更多