用户窗体和范围

UserForm and Range

我有一个 Excel sheet,其中 D 列(第 4 列)是每一行的下拉列表,有 2 个选项:

当我单击“否”时,会弹出一个用户表单,其中包含一个简单的 "text zone" 要求输入一个值和一个 "Submit button" 进行验证。

单击 "Submit button" 时,我希望将 "text zone" 中的值应用到右侧的单元格中:offset(0,1).

示例:D5:"No" -> "Enters 5 in Userform" -> E5:“5”

到目前为止,这是我的代码:

工作sheet:

Private Sub Worksheet_Change(ByVal Target As Range)
    If ActiveCell.Column = 4 Then
        If ActiveCell.Value = "no" Then
            UserForm1.Show
        End If
    End If
End Sub

用户表单:

Private Sub CommandButton1_Click()
    ActiveCell.Offset(0, 1).Value = TextBox1.Value
    UserForm1.Hide
End Sub

如果我将 UserForm1.Hide 放在 ActiveCell 之前,它会执行我想要的操作,但用户窗体不会关闭。 如果我取出 ActiveCell,用户窗体将关闭,但我似乎无法同时使两者工作。

您正在更改 Worksheet_Change 处理程序 中的单元格 ,这意味着如果您没有阻止 UI 的表单,您d 快速将调用堆栈和 运行 炸成 "Out of stack space" 错误,也称为... 堆栈溢出.

您需要防止 Worksheet_Change 处理程序递归调用自身。

这可以通过在进行更改之前关闭 Application.EnableEvents 并在之后重新打开来完成:

Application.EnableEvents = False
ActiveCell.Offset(0, 1).Value = TextBox1.Value
Application.EnableEvents = True

现在,看看这有什么问题?表单如何知道它是从 Worksheet_Change 处理程序调用的,因此它需要切换 Application.EnableEvents?它不知道 - 现在,它假设它。

这是个问题,只是因为表单运行正在显示。翻转过来,让表单尽可能愚蠢,并让 Worksheet_Change 处理程序负责进行 sheet 更改 切换 Application.EnableEvents 状态:

Private Sub Worksheet_Change(ByVal Target As Range)
    If Target.Column = 4 And Not IsError(Target.Value) Then
        If Target.Value = "no" Then
            With New UserForm1
                .Show
                If .Proceed Then
                    Application.EnableEvents = False
                    Target.Offset(0, 1).Value = .Contents
                    Application.EnableEvents = True
                End If
            End With
        End If
    End If
End Sub

几件事:

  1. 触发事件的单元格是 Target - 在 ActiveCell.
  2. 上使用 that
  3. 如果该单元格的值为 #N/A 或任何其他单元格错误值,您的代码就会出错。使用 IsError 验证首先将单元格的值与任何内容进行比较是否安全。
  4. 表单现在需要 ProceedContents 属性,不能自毁。
  5. 调用代码不关心任何文本框:它不知道 Contents 是如何填充的 - 这是表单的关注点。

那么现在表单的代码隐藏是什么样子的?

Option Explicit
Private mProceed As Boolean
Private mContents As String

Public Property Get Proceed() As Boolean
    Proceed = mProceed
End Property

Public Property Get Contents() As String
    Contents = mContents
End Property

Private Sub TextBox1_Change()
    mContents = TextBox1.value
End Sub

Private Sub CommandButton1_Click()
    mProceed = True
    Me.Hide
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If CloseMode = VbQueryClose.vbFormControlMenu Then
        Cancel = True
        Me.Hide
    End If
End Sub

现在所有表单所做的就是收集数据,并将其公开以供调用代码查看:它不知道或不关心任何 ActiveCell 或工作 sheet - 它收集数据,并将其公开以供调用代码查看。 不多也不少.