Why does Showing a UserForm as Modal Stop Code Execution? - vba

The following VBA code stops at Me.Show. From my tests, it seems that Me.Show stops all code execution, even if the code is inside the UserForm.
This part is outside the UserForm:
Public Sub TestProgress()
Dim objProgress As New UserForm1
objProgress.ShowProgress
Unload objProgress
End Sub
This part is inside the UserForm:
Private Sub ShowProgress()
Me.Show vbModal
Dim intSecond As Integer
For intSecond = 1 To 5
Application.Wait Now + TimeValue("0:00:01")
Me.ProgressBar1.Value = intSecond / 5 * 100
Next intSecond
Me.Hide
End Sub
The code stops at Me.Show, after the UserForm is displayed. There is no error, it just discontinues executing code. It seems that the only way to execute code inside a modal UserForm in VBA is to include it in the UserForm_Activate procedure like this:
This part is outside the UserForm:
Public Sub TestProgress()
Dim objProgress As New UserForm1
Load objProgress
Unload objProgress
End Sub
This part is inside the UserForm:
Private Sub UserForm_Initialize()
Me.Show vbModal
End Sub
Private Sub UserForm_Activate()
Dim intSecond As Integer
For intSecond = 1 To 5
Application.Wait Now + TimeValue("0:00:01")
Me.ProgressBar1.Value = intSecond / 5 * 100
Next intSecond
Me.Hide
End Sub
Of course, I can't put Me.Show inside UserForm_Activate because that procedure only fires after the UserForm Show event.
The documentation for UserForm.ShowModal says "When a UserForm is modal, the user must supply information or close the UserForm before using any other part of the application. No subsequent code is executed until the UserForm is hidden or unloaded."
I am trying to use a modal UseForm as a progress bar to prevent the user from interacting with the application while a process runs. But this will be difficult to accomplish if all my code has to be within the UserForm_Activate procedure.
Am I missing something here? Why does all code execution stop at Me.Show?

When the form is displayed with vbModal, the code will suspend execution and wait for user interaction with the form. For example clicking a button or using a dropdown.
If you update the form property
ShowModal = False
and remove vbModal from your code. This will allow code execution to continue when the form is displayed.

I was searching for an answer to why I was getting the following error:
Run time error '5': Invalid procedure call or argument
when running this line of code:
UserForm1.Show True
even though this line works:
UserForm1.Show False
Of course. True is not the same as vbModal! So the simple answer is to use the correct enumerations:
UserForm1.Show vbModal
UserForm1.Show vbModeless

I think I figured this out.
After Me.Show the UserForm_Activate event fires. If there is no code in the UserForm_Activate procedure nothing will happen because VBA is waiting for Me.Hide.
So the order of events is: Me.Show > UserForm_Activate > Me.Hide
Any code that I want to run must be in the UserForm_Activate procedure and must be before Me.Hide.
The structure is very strict, but I may be able to use that structure to my advantage.

Related

Macro Freezes when ListBox is clicked

I'm using Office Professional Plus 2019. I have a ListBox that sometimes freezes the macro. The UserForm (AffDateSelector) has a ListBox (DateSelectionList) and a Button (Continue). The UserForm is called from the driving Sub with AffDateSelector.show. The code for the form is below:
Option Explicit
Private Sub Continue_Click()
Dim lngCurrItem As Long
'Assign the selected value to the public variable strClassDate.
For lngCurrItem = 0 To DateSelectionList.ListCount - 1
If DateSelectionList.Selected(lngCurrItem) = True Then
strClassDate = DateSelectionList.List(lngCurrItem)
End If
Next lngCurrItem
'Unload the form and return to the calling Sub.
Unload Me
End Sub
Private Sub DateSelectionList_Click()
End Sub
Private Sub UserForm_Click()
End Sub
Private Sub UserForm_initialize()
'Load ListBoxAccounts with the public variable strDateArray
With DateSelectionList
.List = strDateArray
End With
'Default the first row in DateSelectionList to be selected.
DateSelectionList.Selected(0) = True
End Sub
I've been using F8 to step through the code. When the form is shown, the UserForm_initialize() sub is executed and the data display properly. If the user does nothing else except click the Continue button, the Continue_Click() sub gets executed, the variable strClassDate is populated, control is returned to the calling sub and all is well.
The problem comes when the user selects an item other than the one defaulted in the list. Once the user clicks on the ListForm, the sub DateSelectionList_Click() is executed, but there's nothing in that sub, so the macro freezes. If I put in "dummy code" into DateSelectionlist_Click() (e.g. strClassDate = strClassDate) that line is executed and then the macro freezes when the End Sub statement is executed.
How can I allow the user to click on a non-defaulted item in the list, keep the form displayed, and wait for the Continue button to be pressed?
The code was fine. I had a lot of windows open and the pop-up didn't appear I was running the code from the VB editor but the pop-up never appeared. When I pressed Alt + Tab it didn't appear in that list either.

Is it possible to display a userform and executes a code bellow the .show method?

I want to display Userform2 from Userform1 then continue executing some code in Userform1 WITHOUT unloading Userform1
Private Sub CommandButton1_Click()
UserForm2.Show
x = 1
MsgBox x
End Sub
Not sure that's what you want, but this would do what you describe:
UserForm2.Show vbModeless
Note that UserForm2 isn't modal anymore, which means the user can click outside the form, and even end up hiding it behind UserForm1.
Modal forms return execution to the caller when they're hidden/closed or destroyed, modeless forms return execution to the caller immediately (Initialize and Activate handlers will run first though).
Assuming you mean without unloading Userform2...
Userform1 will wait for Userform2 to finish... however you can do something like this:
Userform1:
Private Sub CommandButton1_Click()
UserForm2.Show
End Sub
Sub uf1msgbox()
X = 1
MsgBox X
End Sub
Userform2:
Private Sub CommandButton1_Click()
UserForm1.uf1msgbox
End Sub

VBA - UserForm Button (Macro) to Continue running the sub

I have a sub called as sub1() which activates the userform1 through the command userform1.show()
This userform1 has a button called as continue. On clicking that button continue - A Macro called as Private Sub continuebutton() gets activated.
I want to program in such a way that it redirects to the line after userform1.show() in sub1().
Is it something that can be done?
Theoretically, what you want is possible, if you do it like this:
In the UserForm:
Private Sub btnContinue_Click()
flag = True
Debug.Print "I continue ..."
sub1
End Sub
In a module:
Public flag As Boolean
Public Sub sub1()
If flag Then
Debug.Print "sub 1 continues here..."
Else
flag = False
UserForm1.Show
Exit Sub
End If
End Sub
It will work exactly as intended, BUT it is not a good practice to work this way. (Some people may throw stones at you for using public variables at all in VBA.) Here are two articles, that give better ideas:
https://rubberduckvba.wordpress.com/2017/10/25/userform1-show/
Disclaimer - this one is mine:
http://www.vitoshacademy.com/vba-the-perfect-userform-in-vba/
On the form properties for userform1, set its "Modal" property to true.
When the form opens, it will have exclusive focus, and the code in sub1 will not continue running until after it closes. This may be the solution you need.
In the code below, the msgbox will only appear once userform1 closes:
sub sub1()
userform1.show()
msgbox "Now continuing with sub1"
end sub
No way as long as you show the form.
If you show the form modal, the calling routine continues if (and only if) the form is closed.
If you show the form non-modal, the code continues to run directly after the show - so it's already done.
So either you have to close the form when the user clicks the "continue..." button to let the calling macro continue or you have to split your code into two routines and call the second on button-click.
You can change your Sub1 as follows:
Sub sub1(Optional Continue As Boolean)
If Continue = True Then
DoSomeStuff
Exit Sub
End If
userform1.show
End sub
And then, you can call your sub1 using:
Private Sub continuebutton()
Call sub1(True)
End Sub
Hope it helps
If you don't want to go with the 'Modal Form' solution, you could add a subroutine to your main module, and call it when required. So, in userform1, you have:
sub sub1()
userform1.show()
end sub
public sub sub2()
msgbox "Now continuing..."
end sub
And then in userform1, set some code on its onClose event:
Private Sub continuebutton()
Call sub2()
end sub

VBA combobox additem and getting a runtime error 70

I have created a userform that has two buttons on it. One is called CmdCon6 and the other is called CmdLbs6. When clicked, they are suppose to close the current userform, pull up another userform, and pull values from the 4th column in sheet18 and add them to a combobox named x48 (both of the new userforms have a combobox named x48)in the new userform. The range of cell values to be added to the combobox x48 will flucuate, but never exceed 20 (hence why I added a loop). Everything works great and does what it is suppose to do when I click the CmdCon6 button, but when I click the CmdLbs button, it gives me a run-time error '70' Permission denied and highlights the 20th line of code (line between the If and end if in the Sub CmdLbs_Click()).
I have tried to change the name of the combobox x48 in the frmInputLbs6 userform and keep it as x48 for the frmInputCon6 userform, but I still received the same error.
Any suggestions to fix this issue? I'm stumped, and can't think of a way around it. Thanks in advance!
Private Sub CmdCon6_Click()
Unload Me
For x = 1 To 20
If Sheet18.Cells(x, 4).Value <> "" Then
frmInputCon6.x48.AddItem Sheet18.Cells(x, 4)
End If
Next x
frmInputCon6.Show
End Sub
Private Sub CmdLbs6_Click()
Unload Me
For x = 1 To 20
If Sheet18.Cells(x, 4).Value <> "" Then
frmInputLbs6.x48.AddItem Sheet18.Cells(x, 4)
End If
Next x
frmInputLbs6.Show
End Sub
Controls on UserForms are private by default. You need to access them through the Controls collection:
Private Sub CmdLbs6_Click()
Unload Me
For x = 1 To 20
If Sheet18.Cells(x, 4).Value <> "" Then
frmInputLbs6.Controls("x48").AddItem Sheet18.Cells(x, 4)
End If
Next x
frmInputLbs6.Show
End Sub
I'd also note that although you mention that "they are suppose to close the current userform", this isn't what happens. Your forms also aren't actually being fully unloaded until the other form is closed. The .Show method defaults to modal so in the code above frmInputCon6 doesn't fully unload until after frmInputLbs6 is closed.
Just something to keep in mind, because it really messes up your event stack. You can see the results by with this simple test code. Add UserForm1 and UserForm2, and put a button on each of them and the following code:
UserForm1:
Private Sub CommandButton1_Click()
Unload Me
UserForm2.Show
End Sub '<--Put a breakpoint here.
Private Sub UserForm_Terminate()
Debug.Print "UserForm1 closed"
End Sub
UserForm2:
Private Sub CommandButton1_Click()
Unload Me
UserForm1.Show
End Sub '<--Put a breakpoint here.
Private Sub UserForm_Terminate()
Debug.Print "UserForm2 closed"
End Sub
Put a breakpoint on the End Subs of each Click() event, fire up one of the forms and hit the buttons to hop back and forth a few times. Then close one of the forms and count how many times you hit the breakpoints before you actually exit.

PowerPoint displays TWO MsgBox concurrently

Weird behavior when loading an addin from the Developer | AddIns ribbon...
Observed the add-in's Auto_Open procedure fires first, but WHILE that message box is displayed (which should under normal circumstances pause execution awaiting user input -- dismissal of the MsgBox), the ribbon's onLoad event also fires (apparently upon clicking anywhere in the first message box), displaying another message box concurrently!
Have you ever seen TWO message boxes displayed at the same time? These are not userforms displayed modelessly, they are simple MsgBox prompts.
What could explain this behavior? And what can prevent it?
Oddly enough, once the Add-in is loaded, the events fire with what I would consider "normal" sequential order: first the Auto_open, and then the onLoad, but only after you have dismissed the first msgbox.
Ribbon OnLoad procedure:
'This procedure hooks to the ChartBuilder_PPT Add-in menu
Option Private Module
Option Explicit
Private rib As IRibbonUI
''Callback for customUI.onLoad
Sub RibbonOnLoad(ribbon As IRibbonUI)
MsgBox "RibbonOnLoad"
Set rib = ribbon
'Call UPDATE(rib)
End Sub
In another module,
Option Explicit
Const AddInName As String = "ChartBuilder_PPT.ppam"
Const ShortName As String = "ChartBuilder_PPT"
Sub Auto_Open()
MsgBox "Auto_Open"
Call UnloadCB
End Sub
Sub Auto_Close()
Call UnloadCB
End Sub
Sub UnloadCB()
If AddInExists Then
Application.AddIns(ShortName).Loaded = msoFalse
Else
End If
Function AddInExists() As Boolean
Dim a As AddIn
For Each a In Application.AddIns
If a.Name = ShortName Then
AddInExists = True
a.AutoLoad = msoCTrue
Exit For
End If
Next
End Function