Excel 用户表单输入失控

Excel Userform Input going haywire

我有以下代码用于名为 SlideSorterStart 的用户表单:

Private Sub Okay1_Click()

Dim startOn As Integer
startOn = SlideSorterStart.Input1
Unload SlideSorterStart

End Sub

Okay1是下面的确定按钮,而Input1是文本框的名称。

我在模块中使用变量startOn如下:

Sub SlideSorter(ByVal 控件作为 IRibbonControl)

Dim first As Long: first = ActiveWindow.Selection.SlideRange.SlideIndex
Dim last As Long: last = ActivePresentation.Slides.Count

SlideSorterStart.Show

For i = first To last

    With ActivePresentation.Slides(i)

        On Error Resume Next
        .Shapes("Squort").Delete

        Dim square As Shape
        Set square = .Shapes.AddShape(msoShapeRectangle, 400, 360, 150, 150)

        With square
            .Name = "Squort"

            With .TextFrame.TextRange
              .Text = startOn
           End With   ' TextFrame

        End With ' Square itself

    End With

    startOn = startOn + 1

Next i

End Sub

出于某种原因,它不是给出等于用户窗体中填写的数字的输出,而是始终从 0 开始第一个,然后下一次函数是 运行,递增数字的幻灯片。

例如,如果有 5 张幻灯片要放这个框,那么第一次,幻灯片 1 的值为 0。下一次为 5。然后是 10,依此类推。

这是什么原因造成的?

您的 startOn 变量必须在模块级别声明为 Public。 它仅采用您为 Okay1_Click() 事件设置的值。

您还应该使用 Option Explicit' in the module whereSub SlideSorter' 运行。这样,将引发有关 startOn 变量未声明的错误。

UserForm1.Show再出击!

您正在显示表单的 默认实例 ,并且在该表单的代码中,您指的是该表单的默认实例:

Dim startOn As Integer
startOn = SlideSorterStart.Input1
Unload SlideSorterStart

SlideSorterStart标识符在这里有双重用途:它是UserForm的数据类型名称class,它是一个项目-scoped global object variable named after that form class.

您永远不应在表单的代码隐藏中引用表单的默认实例。使用 Me 保留标识符来引用 当前实例 (可以是默认的,但也可以是任何其他的)。

startOn = Me.Input1

而且无论您做什么,都不要 Unload 正在显示该表单的表单,您需要稍后访问其实例状态(例如,用户提供的控件内容)。

按如下方式更改表单后面的代码:

Option Explicit
Private StartAtSlideIndex As Long
Private HasCancelled As Boolean

Public Property Get StartSlideIndex() As Long
    StartSlideIndex = StartAtSlideIndex
End Property

Public Property Get IsCancelled() As Boolean
    IsCancelled = HasCancelled
End Property

Private Sub Input1_Change()
    On Error Resume Next 'index will be 0 if can't convert text value
    StartAtSlideIndex = CLng(Input1.Text)
    On Error GoTo 0
End Sub

Private Sub Okay1_Click()
    Me.Hide
End Sub

Private Sub OnCancel()
    HasCancelled = True
    Me.Hide
End Sub

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

请注意,在表单的代码隐藏中 无处 表单是否被销毁,或者有任何机会被销毁:这就是我们需要处理 QueryClose 的原因,如果用户通过单击红色 "X" 按钮取消对话框,防止表单实例被销毁 - 由于用户总是可以通过这样做自由取消任何对话框,显示表单的代码需要知道是否对话被取消。如果您决定添加一个 Cancel 按钮,您只需要使其 Click 句柄调用 OnCancel 方法。

因此,无论表单如何关闭,我们都只会 Hide 它,并让调用代码销毁它。

SlideSorterStart.Show

这显示了表单的默认实例。避免这样做。

With New SlideSorterStart '<~ form instance gets created here
    .Show
    If .IsCancelled Then Exit Sub

    Dim startOn As Long
    startOn = .StartSlideIndex
End With '<~ form instance gets destroyed here

表单的 Initialize 处理程序,如果存在,将在 New SlideSorterStart 调用(在对象引用被生成到 With 块之前);如果存在,将在 End With.

处调用表单的 Terminate 处理程序

当您使用表单的默认实例时,这两个关键对象生命周期事件的确切时间是不确定的:这就是为什么您需要不惜一切代价避免它,并完全控制您正在使用的对象,以及何时以及如何销毁它们。

您得到 0 的原因是表单正在系统地自行卸载;默认实例与其状态一起被销毁,然后在下次引用它时再次重新创建它,但随后它以默认初始状态重新创建,...这意味着您实际上失去了用户的输入。