All,
I'm struggling with passing input variables from a userform to a module/sub. I've searched the form, but can't find a matching example.
E.g.
Form 1: Two input fields named TextBoxA and TextBoxB and 1 Submit button.
Module 1: some code in which I would like to use the input variables of TextBoxA and TextBoxB
Code in userform:
Private Sub CommandButton1_Click()
pass = myuserform.TextBoxA
End Sub
Who can point me in the right direction. Much appreciated.
Setup a Method which accepts two parameters in a Standard Module and pass the TextBox values as arguments:
Public Sub TestMethod(ByVal param1 As Variant, ByVal param2 As Variant)
MsgBox "You passed: " & param1 & " and " & param2
End Sub
Then you only need to call the method in your button's Click event:
Private Sub CommandButton1_Click()
TestMethod myuserform.TextBoxA.Value, myuserform.TextBoxB.Value
End Sub
Related
I'm trying to reduce repetition in my Userform Code. I've tried writing a sub that sets a SpinButton and Textbox values equal to a defined default value. How do I pass the Spinbutton/Textbox as parameters in a private sub?
Referring to them directly brings up a type mismatch error.
Here's the code that doesn't work:
Private Sub UserForm_Initialize()
Call Default_Setter(TextBox_1, SpinButton_1, 0)
End Sub
Private Sub Default_Setter(InputTextBox As TextBox, InputSpinButton As SpinButton, InputDefaultValue As Integer)
InputTextBox.Locked = True
InputSpinButton.Value = InputDefaultValue
InputTextBox.Text = InputSpinButton.Value
InputTextBox.ControlTipText = "Value between " & InputSpinButton.Min & " and " & InputSpinButton.Max
End Sub
I developed many UDFs and macros in VBA for Excel 2016. One of my macros uses an Inputbox to get data used subsequently by the macro. I want to replace the Inputbox with a user form. I have created the user form, with one text box. I want to activate the user form, populating the text box with the default data, and return the text box data to the macro when OK is selected. I have searched extensively for an end-to-end example for all the the code needed to do this, with no luck. Does an example for this simple problem exist?
Add a Property to your user form. For this answer, let us use the following code within the user form.
Public Property Get MyResult() As String
' You may want to do any manipulation here
' including converting to a number, in which case the return type should be changed (*)
MyResult = TextBox1.Text
End Property
(*) If you are doing conversion, you can have another function in your user form to disable the "OK" button until they have valid convertible data in the text box.
You also want to know if they have hit "Cancel"
Public Property Get Cancelled() As Boolean
Cancelled = pCancelled ' Declare pCancelled as a Boolean in the scope of the form
End Property
Public Sub CancelButton_Click() ' Standard click event for the button
pCancelled = True
Me.Hide
End Sub
Public Sub OKButton_Click() ' Standard click event for the button
pCancelled = False
Me.Hide
End Sub
In your calling macro
MyForm.Show ' This is modal, so will block execution until a response is provided
If Not MyForm.Cancelled Then
Debug.Print MyForm.MyResult
'Do something with MyForm.MyResult
End If
UnLoad MyForm ' assuming you do not want to re-use this form as part of your logic.
There is an example of how you can pass the value to a form and get the result back. The approach uses Scripting.Dictionary object created within standard module scope and passed to userform to allow values to be changed. So it makes possible to send the default values to userform, and keep the result values in the dictionary even after the userform is closed and unloaded. You may have multiple values, just add the necessary quantity of keys to the dictionary, e. g. oData("property1"), oData("property2"), etc.
Add a standard module to the project and put the below code into it:
Option Explicit
Sub Test()
Dim oData
' Set default value and show form
Set oData = CreateObject("Scripting.Dictionary")
oData("") = "Some default text"
UserForm1.ShowForm oData
' Wait until user close form
Do While IsUserFormLoaded("UserForm1")
DoEvents
Loop
' Output returned value
MsgBox oData("")
End Sub
Function IsUserFormLoaded(UserFormName As String) As Boolean
Dim oUF As Object
For Each oUF In UserForms
If LCase(oUF.Name) = LCase(UserFormName) Then
IsUserFormLoaded = True
Exit Function
End If
Next
End Function
Add a userform module named UserForm1 to the project, place controls as shown:
And put the below code into the userform module :
Private opData
Public Sub ShowForm(oData)
Set opData = oData
Me.TextBox1.Value = opData("")
Me.Show
End Sub
Private Sub UserForm_Initialize()
If TypeName(opData) <> "Dictionary" Then Set opData = CreateObject("Scripting.Dictionary")
End Sub
Private Sub CommandButton1_Click()
Unload Me
End Sub
Private Sub CommandButton2_Click()
opData("") = Me.TextBox1.Value
Unload Me
End Sub
How do I feed parameters into a sub? When I run a Macro, the sub just runs without asking for any input.
Sub do_something(input as integer)
xxxxxx
end sub
I know I can use the sub in another sub/function where I can give it a input. Like the following,
sub caller_function()
call do_something(1)
end sub
Is there another way to use do_something?
Thanks in advance!
Set up your sub something like this:
Sub do_something(param As Integer)
MsgBox "The parameter passed to this Sub is " & param
End Sub
To call it:
do_something 1
If you want more than one parameter..
Sub do_something(param1 As Integer, param2 As String)
MsgBox "The parameters passed to this Sub is " & param1 & " and " & param2
End Sub
To call it:
do_something 4,"Hello"
Or for a bit or reading chaos:
do_something param2:="Hello", param1:=4
The code that you provided does not compile. Maybe you were trying to provide a simple example...?
It does not compile because Input is a reserved word, used to read data from a file object. You cannot use it as a variable name. (I have assumed that "xxxxx" is indicating where code should go, i.e. 'Do something here.)
The following code works, but you cannot run it without passing a value:
Sub do_something(i As Integer)
MsgBox i
End Sub
If you want to make the variable optional, you can add the Optional keyword. Then it will run even if a value is not passed:
Sub do_something(Optional i As Integer)
MsgBox i
End Sub
You could also use a globally scoped variable, which would allow the sub to run without directly supplying a value:
Option Explicit
Public i As Integer
Sub do_something()
MsgBox i
End Sub
Regarding Input: https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/inputstatement
With a single argument, you don't use parenthesis
do_something 1
I have little VB knowledge but was hoping to figure this out without any help, but alas!
I have a spread sheet that shows the layout of HP blades inside a chassis. Currently I have set-up a macro (I right-clicked view code on the sheet) whereby you click on a specific 'blade' (really a few merged cells) and an inputbox appears, asking the user which IP they'd like - this then looks for exact matches (e.g. O - OAM, I - iLO) and depending on the option selected - assigns a value in a case statement. For example: O - OAM IP would assign the number 2 - which performs a VLOOKUP and looks in column 2 to return an IP address to the users clipboard.
I'm trying to change the inputbox to a userform. I've created one and have a simple dropdown with the available options.
I'm struggling to export the users selection into a variable to be used later on.
All of my efforts so far result in a variable being assigned following completion of the 'userform' and pressing the 'OK' button. But that variable is then not assigned in the sheet's code area. I assume it's something to do with scope and assignment.
Code for the worksheet
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Target, Range("D57:H80")) Is Nothing Then
Dim HostName As String
Dim ColIndex As Integer
Dim NodeIP As String
HostName = ActiveCell.Value
ufrmIPSelection.Show
'uOption = InputBox("Option Required" & vbCrLf & "o = OAM IP" & vbCrLf & "i = iLO IP", "User selection required")
'uOption
MsgBox "Option captured is " & uOption
Select Case uOption
Case "OAM IP"
ColIndex = 2
Case "iLO IP"
ColIndex = 3
End Select
MsgBox "Column referenced is " & ColIndex
NodeIP = Application.VLookup(HostName, Sheet7.Range("A1:C503"), ColIndex, False)
MsgBox "Hostname is " & HostName
MsgBox "Node IP is " & NodeIP
End If
End Sub
and code for the userform OK button
Public Sub cmdOK_Click()
uOption = cboIP.Value
MsgBox "Combo Box Value " & uOption
'Above line used to see if variable has been assigned
Unload Me
End Sub
Thanks,
Unload Me essentially says "destroy this object". But a form's default instance isn't just any other object - it's a global instance that's automatically re-created by VBA whenever it's referenced again, if it's ever destroyed. But the re-created instance can't magically remember what the previous state was: the re-created instance therefore has whatever the inital / design-time state was.
Another thing that destroys a form and its state, is the little [X] button in the top-right corner.
A modal form is essentially a dialog - and a dialog can either be accepted, or cancelled. When the user dismisses your dialog by clicking that little [X] button, they're telling your program that they wish to cancel whatever that dialog was intended to be doing.
For this reason, it's important that a dialog does nothing other than collect data from the user. The basic code-behind boilerplate for a UserForm with an Ok and a Cancel button, might look like this:
Option Explicit
Private cancelled As Boolean
Public Property Get IsCancelled() As Boolean
IsCancelled = cancelled
End Property
Private Sub OkButton_Click()
Hide
End Sub
Private Sub CancelButton_Click()
OnCancel
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
Cancel = True
OnCancel
End If
End Sub
Private Sub OnCancel()
cancelled = True
Hide
End Sub
Handling the QueryClose event allows us to cancel the destruction of the form and the resetting of its state when the user clicks that [X] button.
Now the calling code can do this:
ufrmIPSelection.Show
If ufrmIPSelection.IsCancelled Then Exit Sub
But since that's storing the state in the form's global/default instance, it's a much better idea to do this:
With New ufrmIPSelection
.Show
If .IsCancelled Then Exit Sub
End With
That way every usage of the form is nicely insulated, and the form instance and its state only live as long as it needs to (i.e. it's gone at End With).
You could go ahead and query the individual controls' state from the calling code, but a much cleaner approach would be to encapsulate that state, using properties (a bit like suggested in 3.1415's answer); these properties only need to be read from the calling code, so only a Propery Get member is needed:
Public Property Get SelectedIPType() As String
SelectedIPType = cboIP.Value
End Property
And now the calling code can do this:
With New ufrmIPSelection
.Show
If .IsCancelled Then Exit Sub
MsgBox .SelectedIPType
End With
The next step would be to extract an actual stateful class to encapsulate the model, so as to completely decouple the form from the data.
Read more about forms' default instance in UserForm1.Show, an article I wrote a few months ago (the basic code-behind boilerplate snippet is taken from there).
You need to add a public property to your form ...
Private m_OptionChoice as String
Property Let OptionChoice(value As String)
m_OptionChoice = value
End Property
Property Get OptionChoice() As String
OptionChoice = m_OptionChoice
End Property
And then change your OK button code to ...
Public Sub cmdOK_Click()
OptionChoice = cboIP.Value
MsgBox "Combo Box Value " & uOption
'Above line used to see if variable has been assigned
Unload Me
End Sub
Now when your form closes, you can access the OptionChoice via ufrmIPSelection.OptionChoice. Eg change ...
MsgBox "Option captured is " & uOption
to ...
MsgBox "Option captured is " & ufrmIPSelection.OptionChoice
Finally, as suggested in Mathieu Guindon's comment below, you need to replace Unload Me with Me.Hide. Thanks Mathieu!
I've inserted a user form into a project that already contains a bunch of modules. By playing around with the code in the user form, I've verified that I can return the value from a combo box.
User form code:
Public SelectedPacking As Integer
Private Sub CancelButton_Click()
UserForm1.Hide
End Sub
Private Sub OKButton_Click()
SelectedPacking = ComboBox1.ListIndex
Call DemoDialogOk
'Return list index value to cell C50
Worksheets("Inputs & Results").Range("C50") = SelectedPacking
Unload UserForm1
End Sub
My problem is that I can't pass this value on to any of the macros written in the modules.
Module code:
Public Sub ShowComboBox()
UserForm1.Show
End Sub
Public Sub DemoDialogOk()
ival = SelectedPacking
'Return value of ival (list index value from combo box) to cell C17
Worksheets("Packed bed (Random)").Range("C17") = ival
End Sub
Obviously the module contains more useful code, but I've commented out everything to try and figure out where I'm going wrong. I've been changing some things around, but I still can't get anything to appear in cell C17, so I think I'm missing something fundamental.
I think two options: 1) change DemoDialogueOK to accept variables:
Public Sub DemoDialogOk(SelPack as integer)
' ival = SelectedPacking
Worksheets("Packed bed (Random)").Range("C17") = SelPack
End Sub
Private Sub OKButton_Click()
SelectedPacking = ComboBox1.ListIndex
Call DemoDialogOk(SelectedPacking)
...
End Sub
Or option two: fully qualify the variable from the useform i.e:
Public Sub DemoDialogOk()
ival = ufYourForm.SelectedPacking
...
End Sub
Public variables in userforms don't appear to be as "public" as module level...
Tipping on top of Simon's answer, you could pass the entire userform if you'd like. This would give you access to all the pieces of it and is especially useful if you need to do some validation on, say, different checkboxes being checked or not.
Sub inOurUserForm()
Call inADifferentModule(Me) 'Passes this userform
End Sub
Sub inADifferentModule(ourForm As UserForm1)
'Passed the form, and using it like a class (As whatever the form is called)
If ourForm.chkYes = True Then
'Do something
Else
'Do something else, like
ourForm.chkYes = False 'Because we are passing the object itself _
rather than a copy, at least in my understanding
End If
End Sub
And you don't necessarily need to pass the userform, as you could just reference it as an object itselft e.g.
UserForm1.chkYes
A very easy solution to this would be to declare a variable within the Userform (UserForm1 in this example)
Public Pass as string
This Pass would contain the string where you store the password. Once you store the password, you can hide the form
Me.Hide
Within the module, you can open the Form as modal
UserForm1.Show vbModal
Now after all the code inside the userform is run, the password can be retrieved within the module -
UserForm1.Pass
You can then unload the hidden form
unload UserForm1