I'm trying to build a simple Userform with a single text box with scroll bars for informational messages.
Unfortunately, I'm getting a Run-time Error 438, Object doesn't support this properly or method error.
My testing code is simple
'three different attempts
Userform including different attempts
Dim inString As String
Sub txtBoxVal(passedStr As String)
inString = passedStr
MyMessageBox = inString
End Sub
public Property get txtBoxVal2(passedStr As String) as string
inString = passedStr
MyMessageBox = inString
End Sub
Public Sub update()
MyMessageBox.Text = publicStrVarDeclaredInFunction
End Sub
Private Sub UserForm_Initialize()
End Sub
Sheet1 Code
Public publicVar As String
Private Sub userformTest()
Dim myForm As UserForm
Dim testString As String
testString = "This is a test string"
Set myForm = New MyUserForm
With myForm
' My 3 different way attempts
Call .txtBoxVal(testString)
.update publicVar
Call .txtBoxVal2(testString)
.Show
End With
End Sub
Anyone have any insights where I'm making my mistake?
Assuming that your Userform's name is MyUserForm and MyMessageBox is the TextBox control:
You should Dim myForm As MyUserForm instead of Dim myForm As UserForm.
Your update sub does not have an argument but you passed publicVar in .update publicVar, you will need to declare the argument in your update sub like this:
Public Sub update(argNewText As String)
MyMessageBox.Text = argNewText
End Sub
Call .txtBoxVal2(testString) won't work for 2 reason:
a) txtBoxVal2 is a Property, not a Sub so you do not use Call. You assign value to txtBoxVal2 property by myForm.txtBoxVal2 = "new value"
b) txtBoxVal2 is a Get property so you can only retrieve txtBoxVal2 value, in order to allow txtBoxVal2 be assigned a value, you have to change to Let:
Public Property Let txtBoxVal2(passedStr As String)
inString = passedStr
MyMessageBox = inString
End Property
=====================
I personally prefer to use Let property approach in this case so your code will look something like this:
Sheet1
Private Sub userformTest()
Dim testString As String
testString = "This is a test string"
Dim myForm As MyUserForm
Set myForm = New MyUserForm
myForm.MessageText = testString
End Sub
MyUserForm
Private inString As String
Public Property Let MessageText(passedStr As String)
inString = passedStr
MyMessageBox.Text = inString
End Property
Try this:
Make a simple UserForm called 'MyMsgBox' with a textbox called 'TextBox1' and a command button called 'CmdOK'.
Put this code into the UserForm:
Option Explicit
''' On form show:
Private Sub UserForm_Initialize()
''' Put text into textbox
TextBox1 = pstMessage
'''BEEP (optional)
Beep
End Sub
''' Close form on OK
Private Sub CmdOK_Click(): Unload Me
End Sub
Put this code in any Standard Module:
Option Explicit
Public pstMessage$
Sub ShowMsg()
''' Put the message into pstMessage
pstMessage = "This is a test string"
''' Run MyMsgBox
MyMsgBox.Show
End Sub
Related
I declared projname globally at the top of the module 1. It is assigned in the userform, and is successfully accessed in the createWB sub. However, when I go to access it in the addWindow sub also in module 1, it becomes empty (""). I'm unsure of why this is happening because I thought that since the variable was globally declared I should be able to access it in any sub.
Module 1
Option Explicit
Public outputWorkbook As Workbook
Public globalcounter As Integer
Public projname As String
Public projnum As String
createWB()
Dim uf2 As New UserForm2
uf2.Show
Set outputWorkbook = Workbooks.Add(xlWBATWorksheet)
outputWorkbook.SaveAs Filename:=Environ("userprofile") & "\Desktop\" &
Replace(projname, " ", "") & ".xlsx"
outputWorkbook.Activate
Range("B3") = projname
Range("B4") = projnum
End Sub
addWindow()
Workbooks(Replace(projname, " ", "") + ".xlsx").Activate
End Sub
Userform Code
Public Sub CommandButton1_Click()
projname = Me.TextBox1.Text
projnum = Me.TextBox2.Text
Me.Hide
End Sub
Cells B3 and B4 are assigned the correct value, but the addWindow() line causes a subscript out of range error. When I test it with Debug.Print, I see that projname = "". I also simply tried outputWorkbook.Activate, which did not work either.
Avoid Global Pollution
Unless there is a really good reason to use them, try to avoid global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that.
Instead, try passing your parameters through your various methods as they are needed. This helps prevent errors, makes your code easier to follow, and utilizes composition.
Using your userform to store and expose your properties
Try to instantiate your userform using a With statement so that you have a captured instance of it where you have access to its various properties that you expose. In your case ProjectName and ProjectNumber.
Additionally, there should be a property to check if the userform was canceled or the X button was pressed.
You userform would look something like this:
Option Explicit
Private cancelled As Boolean
Public Property Get ProjectName() As String
ProjectName = TextBox1.Value
End Property
Public Property Get ProjectNumber() As Long
ProjectNumber = TextBox2.Value
End Property
Public Property Get IsCancelled() As Boolean
IsCancelled = cancelled
End Property
Private Sub CommandButton1_Click()
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
Private Sub OnCancel()
cancelled = True
Hide
End Sub
Instantiating the userform
Here is the example of now calling your userform (P.S. Change the name from Userform2). Notice we are capturing our instance of your userform using the With block. Within this block, we have access to the properties we exposed: ProjectName, ProjectNumber, IsCancelled.
Private Sub createWB()
With New UserForm2
.Show
If Not .IsCancelled Then
' Do neccessaray steps here...
' You have access to ProjectName and Project number.
' Pass this to your addWindow method.
addWindow .ProjectName
End If
End With
End Sub
The ProjectName now can be accessed from your userform and passed as a parameter to you addWindow method.
Private Sub addWindow(ByVal projName As String)
Workbooks(Replace(projName, " ", "") + ".xlsx").Activate
End Sub
For more information on using userforms in this way see this helpful Rubberduck Blog Post.
could you try using Module1 as prefix? , jus like in this code
Public Sub CommandButton1_Click()
Module1.projname = Me.TextBox1.Text
Module1.projnum = Me.TextBox2.Text
Me.Hide
End Sub
All,
I have been struggling with this for a while: is it possible to pass an object to a function?
Here is what I am trying to accomplish:
Get the name of which control was pressed on a form (as object?)
Send the control's name to function "MyFunction" (as reference?)
Disable that same control on "MyFunction"
Called from form1:
Private Sub button1_Click()
Dim caller As String
caller = Form1.ActiveControl.Name
MyFunction(caller)
End Sub 'I'm able to pass it as a string
button1_Click calls MyFunction and passes caller to it:
Private Sub MyFunction(caller As String)
caller.Enabled = False
End Sub
I understand this will not work as a string. How could I possibly do it as an actual object?
Thank you!
There is little problem passing an object to a sub:
Private Sub Disable(c As Control)
MsgBox c.Name
c.Enabled = False
End Sub
Private Sub CommandButton1_Click()
Disable CommandButton1
End Sub
Private Sub CommandButton2_Click()
Disable CommandButton2
End Sub
Private Sub CommandButton3_Click()
Disable CommandButton3
End Sub
In the above I created a userform with three buttons, they say who they are when clicked and are then disabled.
Note that
Disable CommandButton1
can be replaced by
Disable Me.ActiveControl
or even just
Disable ActiveControl
You can even use Variant like so (rough example):
Private Sub CommandButton1_Click()
EnableDisable ActiveControl, "disable"
End Sub
Private Sub EnableDisable(control As Variant, status As String)
If status = "enabled" Then
control.Enabled = True
Else
control.Enabled = False
End If
End Sub
John Coleman's example is better than mine, though.
I have the following button on a Form:
Private Sub CommandButton1_Click()
Dim pass As String
pass = UserForm1.TextBox1
Unload UserForm1
End Sub
I then have a Module called Module1:
Public Sub Login()
...
UserForm1.Show
driver.findElementByName("PASSWORD").SendKeys pass
...
End Sub
The idea is whatever password the users enters into the input box will be assigned to the variable pass. What I'm having trouble doing however is passing pass from UserForm1 into Module1's Login sub.
I would of thought adding something like Module1.Login (pass) to my form before I unload it would work, however that doesn't seem to pass anything. Any help would be much appreciated. Thanks.
Don't declare the variable in the userform. Declare it as Public in the module.
Public pass As String
In the Userform
Private Sub CommandButton1_Click()
pass = UserForm1.TextBox1
Unload UserForm1
End Sub
In the Module
Public pass As String
Public Sub Login()
'
'~~> Rest of the code
'
UserForm1.Show
driver.findElementByName("PASSWORD").SendKeys pass
'
'~~> Rest of the code
'
End Sub
You might want to also add an additional check just before calling the driver.find... line?
If Len(Trim(pass)) <> 0 Then
This will ensure that a blank string is not passed.
Siddharth's answer is nice, but relies on globally-scoped variables. There's a better, more OOP-friendly way.
A UserForm is a class module like any other - the only difference is that it has a hidden VB_PredeclaredId attribute set to True, which makes VB create a global-scope object variable named after the class - that's how you can write UserForm1.Show without creating a new instance of the class.
Step away from this, and treat your form as an object instead - expose Property Get members and abstract away the form's controls - the calling code doesn't care about controls anyway:
Option Explicit
Private cancelling As Boolean
Public Property Get UserId() As String
UserId = txtUserId.Text
End Property
Public Property Get Password() As String
Password = txtPassword.Text
End Property
Public Property Get IsCancelled() As Boolean
IsCancelled = cancelling
End Property
Private Sub OkButton_Click()
Me.Hide
End Sub
Private Sub CancelButton_Click()
cancelling = True
Me.Hide
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = VbQueryClose.vbFormControlMenu Then
cancelling = True
Cancel = True
Me.Hide
End If
End Sub
Now the calling code can do this (assuming the UserForm was named LoginPrompt):
With New LoginPrompt
.Show vbModal
If .IsCancelled Then Exit Sub
DoSomething .UserId, .Password
End With
Where DoSomething would be some procedure that requires the two string parameters:
Private Sub DoSomething(ByVal uid As String, ByVal pwd As String)
'work with the parameter values, regardless of where they came from
End Sub
I have created a userform that contains two checkboxes. I would like to be able to do different things depending on whether each box is checked or unchecked. However, it seems like no matter what I do, it will always tell me the original value of the checkboxes (false and false). Here is the code attached to clicking CommandButton1:
Private Sub CommandButton1_Click()
ReadData
End Sub
And here ReadData:
Sub ReadData()
Dim myForm As UserForm
Set myForm = UserForms.Add("ComplaintEntryForm")
Debug.Print (myForm!CheckBox1.Name)
Debug.Print (myForm!CheckBox1.Value)
Debug.Print (myForm!CheckBox2.Name)
Debug.Print (myForm!CheckBox2.Value)
End Sub
No matter how the boxes are checked, the immediate window always shows this:
VBA.UserForms.Add("ComplaintEntryForm").Show
CheckBox1
False
CheckBox2
False
I have a screenshot of the whole operation but it won't let me upload it because I'm a new user.
Try this method to load and show the form (this goes in a normal module):
Sub main()
Dim myForm As ComplaintEntryForm
Set myForm = New ComplaintEntryForm
myForm.Show
Set myForm = Nothing
End Sub
In the UserForm's own module, add the following:
Private Sub CheckBox1_Change()
readData
End Sub
Private Sub CheckBox2_Change()
readData
End Sub
Private Sub UserForm_Initialize()
Me.CheckBox1.Value = True
Me.CheckBox2.Value = False
End Sub
Private Sub readData()
Debug.Print Me.CheckBox1.Name
Debug.Print Me.CheckBox1.Value
Debug.Print Me.CheckBox2.Name
Debug.Print Me.CheckBox2.Value
End Sub
I've initialized the two checkboxes to specific values in the Initialize event. This means we are certain about the state the form will start in
Suppose, for instance, that I want a method that adds a ComboBox. Maybe I try this
Public Sub AddComboBox()
Dim cb As MSForms.ComboBox
Set cb = <Calling form module>.Controls.Add("Forms.ComboBox.1")
End Sub
How can I get <Calling form module>?
As others have said, pass the instance of the form to class method. Unlike others, I'm going to add:
Declare the argument AS
MSForms.UserForm
Pass the parameter ByVal.
If calling from the UserForm itself,
use the Me keyword in the call.
He's a brief example:
' <Module1.bas>
Option Explicit
Sub Main()
UserForm1.Show vbModeless
UserForm2.Show vbModeless
End Sub
' </Module1.bas>
' <UserForm1.frm>
Option Explicit
Private Sub UserForm_Activate()
Dim c As Class1
Set c = New Class1
c.AddComboBox Me
End Sub
' </UserForm1.frm>
' <UserForm2.frm>
Option Explicit
Private Sub UserForm_Activate()
Dim c As Class1
Set c = New Class1
c.AddComboBox Me
End Sub
' </UserForm2.frm>
' <Class1.cls>
Option Explicit
Public Sub AddComboBox(ByVal MSForms_UserForm As MSForms.UserForm)
Dim cb As MSForms.ComboBox
Set cb = MSForms_UserForm.Controls.Add("Forms.ComboBox.1")
End Sub
' </Class1.cls>
I think you're writing this the wrong way. Instead of trying to determine who called the method, just pass the <Calling Form Module> to AddComboBox() as an argument. Like this:
Public Sub CallToAddComboBox()
AddComboBox(<Calling form module>)
End Sub
Public Sub AddComboBox(CallingFormModule as <Module Object Type>)
Dim cb As MSForms.ComboBox
Set cb = CallingFormModule.Controls.Add("Forms.ComboBox.1")
End Sub