UserForm和Range

时间:2018-06-18 14:58:17

标签: excel vba range userform worksheet

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

  • 没有

当我点击“否”时,我会弹出一个Userform,其中包含一个简单的"文本区域"要求输入一个值和一个"提交按钮"验证。

当"提交按钮"单击,我想要来自"文本区域的值#34;要在右边的单元格中实现:offset(0,1)。

示例:D5:"否" - > "在Userform"中输入5 - > E5:" 5"

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

工作表:

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

UserForm:

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

如果我将UserForm1.Hide放在ActiveCell之前,它会按照我想要的方式执行,但UserForm不会关闭。 如果我取出ActiveCell,UserForm会关闭,但我似乎无法同时使用它们。

1 个答案:

答案 0 :(得分:1)

您正在更改Worksheet_Change处理程序中的单元格,这意味着如果您没有用于阻止UI的表单,您很快就会破坏调用堆栈并遇到“Out of堆栈空间“错误,也称为... 堆栈溢出

您需要阻止Worksheet_Change处理程序以递归方式调用自身。

这可以通过在进行更改之前关闭Application.EnableEvents并在之后切换回来来完成:

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

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

这是一个问题,只是因为表单正在运行show 。翻转周围的东西,并使表单保持尽可能愚蠢,并使Worksheet_Change处理程序负责使表单更改切换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. 如果该单元格的值为#N/A或任何其他单元格错误值,则代码会爆炸。使用IsError验证是否可以安全地将单元格的值与任何内容进行比较。
  3. 表单现在需要ProceedContents属性,不允许自毁。
  4. 调用代码不关心任何文本框:它不知道Contents是如何填充的 - 这是表单的关注点。
  5. 那么表单的代码隐藏现在会是什么样的呢?

    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或工作表 - 它收集数据,并为调用公开它代码看。 没有更多,没有更少