首页 > 解决方案 > Excel 用户表单输入失控

问题描述

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

Private Sub Okay1_Click()

Dim startOn As Integer
startOn = SlideSorterStart.Input1
Unload SlideSorterStart

End Sub

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

在此处输入图像描述

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

Sub SlideSorter(ByVal control As 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,依此类推。

这是什么原因造成的?

标签: vbapowerpointuserform

解决方案


UserForm1.Show再次来袭!

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

Dim startOn As Integer
startOn = SlideSorterStart.Input1
Unload SlideSorterStart

标识符在SlideSorterStart这里有双重用途:它是一个UserForm类的数据类型名称,是一个以该表单类命名的项目范围的全局对象变量。

您永远不应该在该表单的代码隐藏中引用该表单的默认实例。使用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块之前);如果存在,Terminate表单的处理程序将在 处调用End With

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

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


推荐阅读