I have a Userform setup to collect user input on creating a directory with subfolders and files. The input is through the use of a Treeview, and the file structure is predetermined with default selections Checked on or off. The user can toggle the creation of these subfolders and files to suit their needs. I added the options to "Select All" and "Clear All", something easy to do with Treeview Nodes.I also wanted to give users the option to restore the default selections. To that end, I added a "Default Selection" CommandButton that unloads the Userform and then load a fresh instance.
During testing, I noticed a strange bug in my code. There is a Msgbox that pops up to tell me that the script has worked without any errors. However, for every instance the user clicks "Default Selection", the Msgbox pops up that many more times. For example, if the user clicked "Default Selection" 3 times (I don't know why they would, other than being click happy and accident prone...), the Msgbox pops up 4 times; once for the successful test, and 3 more times for each time the button was clicked.
This is the Userform code stripped down to just a single CommandButton cmdDefaultSelect:
Option Explicit
Private Sub cmdDefaultSelect_Click()
'Reset default node checked values by reloading form
Unload Me
Call TestUnloadUserform
End Sub
...and here is the test module with the same symptoms:
Option Explicit
Public Sub TestUnloadUserform()
Dim frM As frmTest
Set frM = SetupTestFrm()
frM.Show vbModal
'Unload userform if it's already loaded
'This sub first loads the form
'Once the form is unloaded by cmdDefaultSelect_Click,
'the script continues to run from here, immediately
'after frM.Show
'If it's not unloaded here, then there is usually an error
If Not frM Is Nothing Then
Set frM = Nothing
End If
MsgBox _
Prompt:="Test complete.", _
Buttons:=vbOKOnly + vbInformation, _
Title:="Great Job"
End Sub
Public Function SetupTestFrm() As frmTest
Set SetupTestFrm = New frmTest
'In actual form, this is where the Treeview and Node properties are set
End Function
For a first draft, it works. The extra code is not included in this example, but the subfolder and files are created without an error. This was also the simplest solution I could come up with to essentially redraw the Treeview. However, I know I'm not loading/unloading my Userform correctly, and I'm probably going to run into trouble later on as I add more features. I don't want the script to end everytime the Userform is unloaded, but I'm unsure on how to structure the code between the Userform and standard modules.
Related
I'm creating a userform in excel. I've created the form, I've populated it with data using a Listbox that reads from a range in excel.
Now I'm trying to add the ability to right click on a row in the Listbox and then perform an action on that line of data form there. Is this possible? I had assumed it was so I was planning on:
Creating a custom popup menu
Working on setting up some sort of event in the userform on right click
Everything else.
I'm stuck on step 1 above. I started by trying to create a custom right-click menu by using the code from Microsoft's website on "showpopup".
https://learn.microsoft.com/en-us/office/vba/api/office.commandbar.showpopup
Private Sub UserForm_Initialize()
Set myBar = CommandBars _
.Add(Name:="Custom", Position:=msoBarPopup, Temporary:=False)
With myBar
.Controls.Add Type:=msoControlButton, ID:=3
.Controls.Add Type:=msoControlComboBox
End With
myBar.ShowPopup
End Sub
This is my code currently, I created a new userform and just pasted the code verbatim from MS's website. When I run the code I get this error:
"Run-time error: '5': Invalid procedure call or argument"
This is the line that's causing the error:
Set myBar = CommandBars _
.Add(Name:="Custom", Position:=msoBarPopup, Temporary:=False)
I'm at a loss on what the error is. Am I on the wrong track here? Am I just doing this whole thing completely wrong?
The reason for the error is that the CommandBar already exists ... before the line that is erroring, add:
On Error Resume Next
CommandBars.Item("Custom").Delete
On Error GoTo 0
... you should also really run the same lines to delete the CommandBar when your UserForm is no longer in use, maybe in the Terminate event
... also your code, as it stands, shows the CommandBar immediately, you likely want to move the myBar.ShowPopup line elsewhere (eg to the appropriate event handler for the ListBox)
Context
I'm using the Listbox_DblClick event of on an Excel Userform (within a .xlam AddIn) to do a bunch of thing (which mainly update the Userform, but alost does some external logging...). Those actions can take up to a few seconds.
Problem
Excel winbdows are let almost frozen if the user move the mouse cursor outside the Listbox during the ListBox_DblClick event.
Consequences
I cannot click anywhere on the UserForm or ActiveSheet or the VBA window. Even the close button of Excel is unresponding. But nothing seems to be running (the last line of the event has already ran, nothing visible on the CPU...)
To be able to interact with Excel again, I need to either :
move temporarly the mouse cursor back over the ListBox
or change temporarly the current Windows application (like by doing Alt+Tab twice).
Excel is then globally fine. There isn't any other consequence and everything runs OK after this freeze.
What I already checked
It's not related to an EnableEvents or ScreenUpdating issue. Those are correctly managed by the macro. Even adding two lines to set them to True at the end of the event don't resolve the issue.
It's not that the macro is still running. Adding Debug.Print "Finished" at the end of the event show that "Finished" prints normally, while Excel is kept frozen.
Since I don't want the user to launch a new event while the current event is still running, I prevent that by locking/delocking the many ListBox (including the one realted to the event) using ListBox.Locked at the beginning/end of the event. But commenting these lines doesn't resolve the issue.
Minimal Reproductible Exampe
Add an Array on a UserForm
Add the following code
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean)
For n = 1 To 3000
Me.Caption = n
Next n
Me.ListBox1.List = Array("Apple")
End Sub
Private Sub UserForm_Initialize()
Me.ListBox1.List = Array("Apple", "Banana")
End Sub
Double click on "Banana" and moove the mouse cursor
Note
In this post form 2012 (Editing listbox items in Dblclick event freezes Excel unless mouse moves over listbox), the user seems to encounter a similar issue.
I can't get the RowSource property of a list box to update via VBA. From another thread, I found the syntax, so I think this is correct. But, despite not failing, it doesn't do anything to the RowSource property (it remains blank). Below:
frmAddIngredient is the user form.
lbxIngredient is a listbox control in that form.
UniqueIngredients is one of the sheets in the workbook.
NumberOfItems is 1 (in this case).
It doesn't give an error, but it doesn't change anything, either. The form itself is not active at this time. This code is supposed to set up the form for later showing.
frmAddIngredient.lbxIngredient.RowSource = Sheets("UniqueIngredients").Range("A1:A" & CStr(NumberOfItems)).Address
The most recent code is
frmAddIngredient.lbxIngredient.RowSource = "=UniqueIngredients!A1:A1"
but, it still doesn't change anything in the actual form.
Also, can I add a new post, or do I have to continue editing this one and adding stuff?
What you want (as discussed in the comments on your question) is not possible. Setting something by code does not change it's property in the properties window and is only so until your project resets.
Consider a Userform with 2 buttons, with their original name and caption and then in a module paste these 2 subs.
Sub demo1()
UserForm1.CommandButton1.Caption = "Demo 1"
UserForm1.Show
End Sub
Sub demo2()
UserForm1.CommandButton2.Caption = "Demo 2"
UserForm1.Show
End Sub
When you run the first Sub demo1 Button 1's caption has changed but Button 2's caption has not.
Close the Userfom and now run demo2, you'll see that Button 1's caption is back to it's original hard set (properties window) name and that now Button 2 has a different name.
I'm trying to make a standardized process for updating a particular worksheet. I want no user control except for the functions I give them. To do that I have locked sheets and then forms that load with certain macros. One form is designed to remove data from the sheet. It works fine as written and tested, but I've tried to update it so that if you open it without any relevant data to remove, it spits out a dialogue box and then uses Unload Me to close the form. This closes the form but then excel throws an error:
Run-time error '91': Object variable or With block variable not set
The form is loaded from a module that only has the one line:
MyForm.Show
This is where excel is throwing the error from. On initialization of the form, a combobox is filled with values based on the data in the sheet. If the combobox is empty after loading, the form is supposed to throw the dialogue box and then close.
If ComboBox.ListCount = 0 Then
MsgBox "No Data"
Unload Me
End If
How can I perform the check on load without having the error thrown from the Module?
This doesn't actually answer your question. But what I suggest is do the checking in your module code before you actually load the form. Something like:
Sub LoadForm()
If Sheets("Sheet1").Range("A1") = "" Then '<~~ your condition here
MsgBox "No Data"
Else
MyForm.Show
End If
End Sub
Another way would be to place the Unload Me in the Activate event:
Private Sub UserForm_Activate()
...
If ComboBox.ListCount = 0 Then
MsgBox "No Data"
Unload Me
End If
End Sub
The problem happens when you try and unload the userform from within it's initialize event. Because the object hasn't finished initialization yet, it cant be unloaded. The best ways to get around this are either to check conditions before you try to initialize the form, or put your checks and subsequent unload statements into the activate event of the userform. Activate is called whenever the form goes from being hidden to visible, which happens after the form has been completely initialized.
Sub RunForm()
On Error GoTo error
UserForm1.Show
error:
End Sub
I wanted to figure out how you could load every UserForm without having to call Userform1.Show UserForm2.Show etc. This was inspired by comments on this answer: Excel VBA UserForm_Initialize() in Module.
I found this method suggested in a few places:
Sub OpenAllUserForms()
Dim uf As UserForm
For Each uf In UserForms
uf.Show
Next
End Sub
However, no Userforms display regardless of how many are attached to the workbook. When I stepped through the code I determined that the UserForms collection is empty!
How can I load each Userform without having to explicitly show each one?
According to this page: UserForm Object
The UserForms collection is a collection whose elements represent each
loaded UserForm in an application.
Since your Userforms are not loaded they do not appear in the collection. This means you'll need to load the forms a different way. In order obtain the name of each UserForm you will need to allow permission for code to access the Visual Basic Project. Otherwise, the name of the Userform will need to be provided by you.
The following will open every UserForm in the current VBProject.
Sub OpenAllUserForms()
Dim VBComp As Object
For Each VBComp In Application.VBE.ActiveVBProject.VBComponents
If VBComp.Type = 3 Then '3 = vbext_ct_MSForm
VBA.UserForms.Add(VBComp.Name).Show
End If
Next
End Sub
This works because each UserForm is listed in the VBProject as a VBComponent. Once the code determine which components are UserForms, it adds the Userform to the collection and displays it. If you omit the .Show the form will still run the Initialize event, but then immediately go out of scope and vanish.
I've solved the existing problem by applying the line below
In Module:
UserForms(Name).Userform_Initialize
Description:
This will initialize the current UserForm.
Example:
If result = True Then
UserForms(Name).Userform_Initialize
This means that when the result is true, the form will change to the initial state.
P.S Pardon my bad English