Excel Userform Input going haywire

半世苍凉 提交于 2020-01-14 03:17:11

问题


I have the following code for a userform called SlideSorterStart:

Private Sub Okay1_Click()

Dim startOn As Integer
startOn = SlideSorterStart.Input1
Unload SlideSorterStart

End Sub

Okay1 is the OK button here below, while Input1 is the name of the text box.

I use the variable startOn in a module as follows:

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

For some reason, instead of giving an output equal to the number filled into the UserForm, it always starts the first one at 0, and then the next time the function is run, increments by the number of slides.

For instance, if there are 5 slides to put this box on, then the first time, Slide 1 will have a 0. The next time at 5. Then a 10, and so on.

What is causing this?


回答1:


UserForm1.Show strikes again!

You're showing the form's default instance, and inside that form's code, you're referring to the default instance of that form:

Dim startOn As Integer
startOn = SlideSorterStart.Input1
Unload SlideSorterStart

The SlideSorterStart identifier is dual-purposed here: it's the data type name of a UserForm class, and it's a project-scoped global object variable named after that form class.

You should NEVER refer to a form's default instance within that form's code-behind. Use the Me reserved identifier to refer to the current instance (could be the default one, but could be any other too).

startOn = Me.Input1

And whatever you do, NEVER Unload a form while that form is being shown and you need to later access its instance state (for example, the user-provided contents of a control).

Change your form's code behind as follows:

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

Note that nowhere in the form's code-behind does the form get destroyed, or gets any opportunity to be destroyed: that's why we need to handle QueryClose, to prevent the form instance from being destroyed if the user cancels the dialog by clicking the red "X" button - and since the user is always free to cancel any dialog by doing that, the code that's showing the form needs to know whether the dialog was cancelled. If you decide to add a Cancel button, you only need to make its Click handle invoke the OnCancel method.

So, regardless of how the form gets closed, we only ever Hide it, and let the calling code destroy it.

SlideSorterStart.Show

That's showing the form's default instance. Avoid doing that.

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

The Initialize handler of the form, if present will be invoked at New SlideSorterStart (before the object reference is yielded to the With block); if present, the Terminate handler of the form will be invoked at End With.

Exactly when these two critical object lifecycle events are fired when you use the form's default instance, is non-deterministic: that's why you need to avoid it at all costs, and take full control of what objects you're using, and when and how you're destroying them.

The reason why you're getting 0 is because the form is systematically unloading itself; the default instance gets destroyed along with its state, and then it gets re-created again next time it's referenced, but then it's re-created with the default initial state, ...which means you effectively lose the user's input.




回答2:


Your startOn variable must be declared as Public at a module level. It takes the value you set only for Okay1_Click() event.

You should also use Option Explicit' in the module whereSub SlideSorter' runs. In this way an error regarding the startOn variable not being declared will be raised.



来源:https://stackoverflow.com/questions/59180163/excel-userform-input-going-haywire

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!