code screenshot
I've tried several different methods and nothing seems to be working.
One way to make sure you are targeting the right object, is by selecting it in the dropdown list.
Then the Private Sub UserForm_Initialize() sub will be created automatically, and is sure to point in the right direction.
The thing about specifically the UserForm object, compared to the others, is that it will always be called UserForm no matter what the name is, because there will always only be one userform in a userform.
Related
I have a workbook with many worksheets. Some of them must be protected, and some of them unlocked. The tricky part is that a userform created using Excel VBA will be used to add data to one of the protected worksheets, and the code fails when said worksheet is protected because it is supposed to add rows to a table to save the data.
I tried using the following code to fix this:
Sheets("Sales").Protect Password:="123", AllowInsertingRows:=True, UserInterFaceOnly:=True
Although after running it I can manually add rows through the user interface, it seems that the property I am trying to use only works that way, VBA code still can not and new rows to the protected worksheet.
So, the workaround I thought of is to unprotect the worksheet when the userform is initialized, like this:
Private Sub UserForm_Initialize()
Sheets("Sales").Unprotect "123"
End Sub
This way the userform can save all the data without any issues. However, my question is how to properly protected it again. There are two ways of closing the userform, one is with a cancel button that just runs the Unload Me piece of code, and the second one is using the X on the top right corner of the form. I would like to keep those two options available, but also make sure that the worksheet is protected again before the user can try to edit it (it is imposible to select anything with the userform open).
What I am thinking would be the best option is the following code:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
Sheets("Sales").Protect Password:="123"
End Sub
After testing it I believe it works, but I am not sure if this is the proper way of doing this. Maybe there is a specific situation that I am not considering that would end up with the user being able to edit the worksheet that is supposed to be protected. Could please let my know if there are any flaws in my code?
I have a Form in an Excel VBA project that has many textboxes with names I'd need to change. For instance, names in the projects are: Fees01TBX; Fees02TBX; Fees03TBX, and so on. There are 40 textboxes like these, and I need to change the names into a sequence I can manage better. For instance, I'd need to have them with these names: Expenses01; Expenses02, Expenses03, and so on.
I tried to change it using some code like (just for one textbox):
Sub ChgName()
MyForm.Fees01TBX.Name = "Expenses01"
'Doing this for each one would not be a problem, I can array the sequence.
End Sub
I think it should possible to change the name of a textbox in a form by code, but how?
Make sure you ticked Trust Access to the VBA Project Object Model in security settings:
Sub Rename()
With ThisWorkbook.VBProject.VBComponents("MyForm")
.Designer.Controls("Fees01TBX").Name = "Expenses01"
End With
End Sub
But if Fees01TBX had code you have to change this as well, but for 40 TextBoxes you could do it manually
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.
First a little background information:
I am working on a project in Excel. When you open the file, a userform pops up and asks for input in multiple textboxes. It then puts the input in certain cells in the Excel document. Then another userform pops up, and asks for more input, which is inserted in cells as before.
Next, it requests numerical input in textboxes, and this data inserts the respective number of rows under each category in the Excel document.
Now for the problem.
The next thing I want to do is have a userform which dynamically creates a certain number of labels and textboxes based on the numbers inputted by the user in the previous userform. I have already figured out how to bring the variables from one userform to another; now I am having issues with the naming of the dynamically created textboxes. Well, really I know how to name them, but can't figure out how to use the textboxes' .Text attributes as input to cells in the Excel document. I have spent upwards of six hours searching the internet for an explanation of how to do this that I can apply to my project, so now I'm looking for personalized help.
Here's the options I have considered:
Arrays
Controls.Add("txtBoxName" & i) in a For...Next loop
I have actually tried the For...Next loop with Controls.Add,
and had absolutely no luck.
The problem with both of these, while Arrays may be more convenient in the long run, is that (at least as I see it) with both you have to name the textboxes and then call the names, which is a syntax I cannot find anywhere.
Can anyone help? I have no problem posting the relevant code if need be.
Also, if I have overlooked something, feel free to point me in the right direction. I may ask for help understanding it, though! ;)
Thanks,
Dudebird47
Simple example of a button added at run time with code to handle its click event:
Option Explicit
Private WithEvents btn As MSForms.CommandButton
Private Sub btn_Click()
MsgBox "You clicked me"
End Sub
Private Sub UserForm_Initialize()
Set btn = Me.Controls.Add("Forms.CommandButton.1", "some_button", True)
With btn
.Top = 5
.Left = 5
.Width = 75
.Height = 50
.Caption = "click me"
End With
End Sub
ActiveX combobox objects in Excel do not behave well when their ListFillRange refers to a formula-based Named Range (Defined Name).
I think I have encountered other errors and possibly even Excel crashes thanks to this, but right now all that happens is the combobox_change() event is triggered anytime ANY cell in the workbook is changed.
I am not sure if this is really a bug, or if there is a fix, or a workaround. If it is a bug, how do I report it to the Excel people?
And finally, the real meat of my question is "How do I work around this issue best?" I would like to have some formula-based named ranges, but it seems like this won't be possible.
To reproduce this bug, do the following:
Create a new workbook. On Sheet3, create a small table 3 columns across, and several rows high.
Create a named range with this formula (or an equivalent): =OFFSET(Sheet3!$A$2:$C$36,0,0,COUNTA(Sheet3!$A:$A),COUNTA(Sheet3!$4:$4)) To do this use Input>Name>Define. Name the range something like "demoRange"
Go to Sheet1 and create a combobox, (it must be on a separate sheet). (Use the Control Toolbox menu, not the Forms menu).
Click on the Design Mode button (the blue triangle with pencil), then right click on the combo box and go to Properties.
In the properties window for the combobox, change the ListFillRange property so that it points at the named range you created in step 2 ("demoRange").
You may want to change the ColumnCount property to 3, and the ColumnWidths property to "50,50,50"
Set the linkedCell property to cell "A1" by typing A1 in the linkedCell property.
Close the properties window, and double click on the combobox to define its change() event.
Put a Debug.Assert(false) or Msgbox("demo") line in the subroutine for the new combobox's change event.
Exit design mode
important - Now select an item in the combobox. The event should trigger normally the first time. (The bug will not show if you don't do this step--something must be selected in the combobox)
Edit cells anywhere in the workbook [Edit] or any other open workbook [/edit], on any sheet and any location. Each time you edit any cell, (at least for me), the onchange event for the combo box is run.
Again, is this normal, and what is the best alternative for what I am doing? This combo box gets linked to various cells, and is supposed to be a replacement for the tiny font in the data validation dropdowns excel provides by default.
My advice is to never use ListFillRange and LinkedCell. They are just trouble. Fill your listbox with List and use the Change event to write to the cell. Somewhere, maybe the Workbook_Open event, fill the listbox
Private Sub Workbook_Open()
Sheet2.ListBox1.Clear
Sheet2.ListBox1.List = Sheet1.Range("demoRange").Value
End Sub
Then in the change event in the Sheet2 module, check that something was clicked and write it to the cell
Private Sub ListBox1_Change()
If Me.ListBox1.ListIndex >= 0 Then
Sheet2.Range("A1").Value = Me.ListBox1.Value
End If
End Sub
I have a few options available that I am aware of thus far. The best I can come up with is this:
Avoid directly using formula-based named ranges. Instead, define a subroutine that will check whether the defined range "demoRange" should be changed from what its current value is. Run this subroutine on the workbook_open and sheet3_deactivate events. If needed, prompt the user to ask if it's all right to update the named range. [edit] The macro that updates "demoRange" could probably just copy from a "demoRange_FormulaBased" named range into "demoRange" which would be static. [/edit]
This solution works well because you can keep using the linkedcell property, you don't have to use VBA to populate the comboboxes, and the named range can still be used for whatever other purposes it already had. Avoid using the onchange event to run this new subroutine, since it might end up being triggered thousands of times if a user opens the Find/Replace dialog and chooses "Replace All".