Excel VBA: Compile Error: Method or data member not found - vba

EDIT: To clarify, the code seen below is within a module and the UserForm is all contained within its own code.
I have the following code. When I go to run it, Excel throws me a compile error: Method or data member not found and highlights the following piece of code: .showInputsDialog. I have no idea how to resolve this error.
To give more information, the sub sportUserForm is supposed to call up a UserForm sportsUsrFrm. Any help with this issue is greatly appreciated.
Option Explicit
Sub sportUserForm()
Dim sSport As String, sPreference As String
If sportsUsrFrm.showInputsDialog(sSport, sPreference) Then
MsgBox "Your favorite sport is " & sSport & ", and you usually " _
& sPreference & "."
Else
MsgBox "Sorry you don't want to play."
End If
End Sub
Public Function showInputsDialog(sSports As String, sPreference As String) As Boolean
Call Initialize
Me.Show
If Not cancel Then
If optBaseball.Value Then sSport = "Baseball"
ElseIf optBasketball.Value Then sSport = "Basketball"
Elss sSport = "Football"
End If
If optTV.Value Then sPreference = "watch on TV" _
Else: sPreference = "go to games"
End If
showInputsDialog = Not cancel
Unload Me
End Function
UserForm code for sportUsrFrm
Option Explicit
Private Sub cmdCnl_Click()
Me.Hide
cancel = True
End Sub
Private Sub cmdOK_Click()
If Valid Then Me.Hide
cancel = False
End Sub

You're getting the error because showInputsDialog isn't a member of the form, it's a member of the module you're calling it from. You should also be getting compiler errors on these two lines...
Call Initialize
Me.Show
...because you seem to be getting the module and form code mixed up.
That said, you're overthinking this. A UserForm is a class module, and it can be stored in a variable (or in this case, in a With block), and can have properties. I'd add a Cancelled property to the form:
'In sportsUsrFrm
Option Explicit
Private mCancel As Boolean
Public Property Get Cancelled() As Boolean
Cancelled = mCancel
End Property
Private Sub cmdCnl_Click()
Me.Hide
mCancel = True
End Sub
Private Sub cmdOK_Click()
If Valid Then Me.Hide '<-- You still need to implement `Valid`
End Sub
And then call it like this:
Sub sportUserForm()
With New sportsUsrFrm
.Show
Dim sSport As String, sPreference As String
If Not .Cancelled Then
If .optBaseball.Value Then
sSport = "Baseball"
ElseIf .optBasketball.Value Then
sSport = "Basketball"
Else
sSport = "Football"
End If
If .optTV.Value Then
sPreference = "watch on TV"
Else
sPreference = "go to games"
End If
MsgBox "Your favorite sport is " & sSport & ", and you usually " _
& sPreference & "."
Else
MsgBox "Sorry you don't want to play."
End If
End With
End Sub

Related

Making VBA Form specific TextBox accept Numbers only and also "."

I want to block some specific textboxes has numeric values only and accept ".". However, it blocks almost all my textboxes in my userform. I don't understand why. What I forgot in my code?
Private Sub tbxHour_Exit(ByVal Cancel As MSForms.ReturnBoolean)
'Making TextBox accept Numbers only
If Not IsNumeric(tbxHour.Value) Then
MsgBox "only numbers allowed"
Cancel = True
End If
End Sub
Private Sub tbxHour_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
Select Case KeyAscii
Case 46
If InStr(1, tbxHour, ".") > 0 Then KeyAscii = 0
Case 48 To 57
Case Else
KeyAscii = 0
End Select
End Sub
This one worked for me:
Private Sub tbxHour_AfterUpdate()
'Make sure the item is Numeric or has a "." in it
If Not IsNumeric(Me.tbxHour.Text) And Not Me.tbxHour.Text = "." Then
MsgBox "This is illegal!"
Me.tbxHour.Text = ""
End If
End Sub
Short. Simple. Effective and looks like what you're trying to do anyway.
I use this simply NumKeyValidator class for that, to simply prevent invalid input to be supplied by the user:
Option Explicit
Private Const vbKeyDot As Integer = 46
Public Function IsValidKeyAscii(ByVal keyAscii As Integer, ByVal value As String) As Boolean
'returns true if specified keyAscii is a number, or if it's a dot and value doesn't already contain one
IsValidKeyAscii = (keyAscii = vbKeyDot And InStr(1, value, Chr$(vbKeyDot)) = 0) Or (keyAscii >= vbKey0 And keyAscii <= vbKey9)
End Function
You can use it by simply declaring an instance field for it:
Private validator As New NumKeyValidator
And then you use it in each textbox' KeyPress handler, like this:
Private Sub tbxHour_KeyPress(ByVal KeyAscii As MSForms.ReturnInteger)
If Not validator.IsValidKeyAscii(keyAscii, tbxHour.Value) Then keyAscii = 0
End Sub
There's no need to handle Exit and pop a MsgBox then - either the box is empty, or it contains a valid number; you could have an IsValidForm property that returns True if all required textboxes contain numbers, and false otherwise - and then decide that the form's Ok button is disabled until the form is valid.
FWIW that validator class is quite thoroughly tested (using Rubberduck unit tests [disclaimer: I own that open-source VBE add-in project]):
Option Explicit
Option Private Module
'#TestModule
'' uncomment for late-binding:
Private Assert As Object
'' early-binding requires reference to Rubberduck.UnitTesting.tlb:
'Private Assert As New Rubberduck.AssertClass
'#TestMethod
Public Sub DotIsValidForEmptyValue()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("."), vbNullString)
'Assert:
Assert.IsTrue actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub DotIsValidForNonEmptyValueWithoutAnyDots()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("."), "123")
'Assert:
Assert.IsTrue actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub DotIsInvalidWhenValueHasDot()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("."), "123.45")
'Assert:
Assert.IsFalse actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub AllDigitsAreValid()
On Error GoTo TestFail
Dim sut As New NumKeyValidator
Assert.IsTrue sut.IsValidKeyAscii(Asc("0"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("1"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("2"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("3"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("4"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("5"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("6"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("7"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("8"), vbNullString)
Assert.IsTrue sut.IsValidKeyAscii(Asc("9"), vbNullString)
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub AlphaIsInvalid()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("a"), vbNullString)
'Assert:
Assert.IsFalse actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub DollarSignIsInvalid()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("$"), vbNullString)
'Assert:
Assert.IsFalse actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
'#TestMethod
Public Sub NegativeSignIsInvalid()
On Error GoTo TestFail
'Arrange:
Dim actual As Boolean
Dim sut As New NumKeyValidator
'Act:
actual = sut.IsValidKeyAscii(Asc("-"), vbNullString)
'Assert:
Assert.IsFalse actual
TestExit:
Exit Sub
TestFail:
Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
That said I don't see how the code you've shown could ever "block almost all textboxes in your userform".

How to create dynamic naming for each controls on userform in sub routine?

I want to create a routine that when called will perform the function base on the control names dynamically. Here's my code:
Private Sub myControl(counter as string)
If mySecondControl & counter.Value = "Y" Then
myThirdControl & counter.Caption = "Do Something Here" 'Error syntax
End If
End Sub
Private Sub doThis_Change()
myControl("1")
myControl("2")
End Sub
You need to get the Control you want from the form's Controls collection:
If Controls("mySecondControl" & counter).Value = "Y" Then
Controls("myThirdControl" & counter).Caption = "Do Something Here"
End If

How do I implement an event properly in a VBA Context-Menu?

I try to call an Function via an CommandBarButton-Event.
The Code looks finde so far, I also get no Error, but nothing happens. What am I doing wrong?
My Code looks like this:
Private Sub Main()
ContextMenu1
CommandBars("MyListControlContextMenu").ShowPopup
End Sub
Private Sub ContextMenu1()
Dim MenuItem As CommandBarPopup
On Error Resume
NextCommandBars("MyListControlContextMenu").Delete
On Error GoTo 0
With CommandBars.Add(Name:="MyListControlContextMenu", Position:=msoBarPopup, MenuBar:=False, Temporary:=True)
Set MenuItem = .Controls.Add(Type:=msoControlPopup)
With MenuItem
.Caption = "main"
Dim MenuButton As CommandBarButton
Set MenuButton = MenuItem.Controls.Add(Type:=msoControlButton)
With MenuButton
.Caption = "sub"
.OnAction = "Test1"
End With
End With
End With
End Sub
Private Sub Test1()
MsgBox "test", vbOKOnly, "test"
End Sub
It seems like the correct syntax for MS-Access 2010 VBA when implementing an event inside a contextmenu looks like this:
Sub MyContextMenuConstructor()
...
With MenuButton
.Caption = "myCaption"
.OnAction = "=MyFunction('" & myParameter & "')"
End With
...
End Sub
Private Function MyEvent(myInputParameter As String)
MsgBox myInputVariable, vbOKOnly
End Function
Remember:
- What ever called by the Event, needs to be a Function, no Sub;
- It needs to be called with brackets, even if you do not pas a parameter;
- When you call the Function, you must have an '=' before its name and you have to have an apostrophe before and after your Parameter. Also allways place a Whitespace between '&' and '"', since VBA doesn't seem to be that clever.

VBA: subroutine with if statement and returning true or false?

SOLVED!
I have to validate that certain cells are not empty, so I want to create a subroutine and pass the variables I need checked.
This is what I came up with:
Sub errorMessage(errMsg As String, errRange As String)
If Range(errRange) = "" Then
MsgBox errMsg, , "Error:"
Range(errRange).Activate
'this is what i was looking for :doh:, the 'end' line terminates everything..
END
End Sub
Now when I call it from my button, will it actuall end the sub of the button?
i.e.
Private Sub CommandButton1_Click()
Call errorMessage("name is missing", "D4")
'this function shouldn't be called if there was a msgbox displayed with the above call
sendEmail
End Sub
How can i make this happen?
EDIT:
OK So this is how i sovled it, the reason i'm trying to do this is to avoid tons of lines of code in the buttonClick sub, what are your thoughts??
keep in mind that this thing has to check about 25 questions for blanks before executing the sendEmail sub....
Private Sub CommandButton1_Click()
Call validateEntry("Client Name is missing.", "D4")
Call validateEntry("# not complete.", "D5")
Call validateEntry("Address same as CV?", "D6")
Call validateEntry("Number missing.", "D8")
Call validateEntry("Type missing.", "D9")
Call validateEntry("Q1 requires a Yes or No.", "E19")
Call validateEntry("Q2 requires a Yes or No.", "E21")
Call validateEntry("Q3 requires a Yes or No.", "E23")
Call validateEntry("Q4 requires a Yes or No.", "E25")
Call validateEntry("Q5 requires a Date.", "D28")
Call validateEntry("Q6 requires a Yes or No.", "E30")
Call validateEntry("Q7 requires a Yes or No.", "E32")
MsgBox "passed"
'sendEmail
End Sub
Sub validateEntry(errMsg As String, errRange As String)
If Range(errRange) = "" Then
MsgBox errMsg, , "Error:"
Range(errRange).Activate
End
End If
End Sub
So, in your example, you're looking for the "passed" notification to only be sent when there is data in cell D4, right?
This should work:
Private Function errorMessage(errMsg As String, errRange As String) As Boolean
errorMessage = False
If Len(Trim(Range(errRange))) = 0 Then
MsgBox errMsg, , "Error:"
Range(errRange).Activate
errorMessage = True
End If
End Function
Public Sub CommandButton1_Click()
If errorMessage("name is missing", "D4") = False Then
MsgBox "passed"
End If
End Sub
Alternatively, you can handle all MsgBox notifications from within the function, to group similar logic together, and keep the Button Click Event Sub clean:
Private Function errorMessage(errMsg As String, errRange As String)
If Len(Trim(Range(errRange))) = 0 Then
MsgBox errMsg, , "Error:"
Range(errRange).Activate
Else
MsgBox "passed"
End If
End Function
Public Sub CommandButton1_Click()
Call errorMessage("name is missing", "D4")
End Sub
There are a number of misconceptions here.
First, no, it will not end the button routine by default. You will need to handle that within your button.
Next, you're missing an End If somewhere in here:
Sub errorMessage(errMsg As String, errRange As String)
If Range(errRange) = "" Then ' This may not be the best way to check for
' an empty range
MsgBox errMsg, , "Error:"
Range(errRange).Activate
Exit Sub
End Sub
You really don't even want a subroutine in the first place, you want a function that returns a boolean, like this:
Function errorMessage(errMsg As String, errRange As String) as Boolean
' Function returns True if an error occured
errorMessage = False
If Range(errRange) = "" Then
MsgBox errMsg, , "Error:"
Range(errRange).Activate
errorMessage = True
End If
End Sub
And then here:
Private Sub CommandButton1_Click()
If errorMessage("name is missing", "D4") Then
Exit Sub
End If
'this msgbox should not display if the above msgbox is displayed
MsgBox "passed"
' continue on with all of your fun processing here
End Sub

Cannot Run Macro Created with Add-in in Excel

I have create an xla (excel add-in) that have a function to protect the document (so that user could protect the document without knowing the password). This xla is added in every excel file that need this functionality.
when the xla is installed or added to excel, the protect button will be added in last menu. but when i click the button, an error occur show that
"Cannot run the macro Pivot Add-In 0.2'!protectSheet'". The macro may not be available in this workbook or all macros may be disabled."
The code that event handler onclicked is protectSheet, please see the source below:
Could anyone pointed my why this problem occur?
on ThisWorkbook
'main function'
Public Sub protectSheet()
ActiveWorkbook.ActiveSheet.protect Password:="password", AllowUsingPivotTables:=True
End Sub
Public Sub ShowToolbar()
' Assumes toolbar not already loaded '
Application.CommandBars.Add Module1.TOOLBARNAME
AddButton "Proteksi Sheet", "Memproteksi Pivot", 3817, "protectSheet"
' call AddButton more times for more buttons '
With Application.CommandBars(Module1.TOOLBARNAME)
.Visible = True
.Position = msoBarTop
End With
End Sub
Public Sub AddButton(caption As String, tooltip As String, faceId As Long, methodName As String)
Dim Btn As CommandBarButton
Set Btn = Application.CommandBars(Module1.TOOLBARNAME).Controls.Add
With Btn
.Style = msoButtonIcon
.faceId = faceId ' choose from a world of possible images in Excel: see http://www.ozgrid.com/forum/showthread.php?t=39992 '
.OnAction = methodName
.TooltipText = tooltip
End With
End Sub
Public Sub DeleteCommandBar()
Application.CommandBars(TOOLBARNAME).Delete
End Sub
'called when add-in installed
Private Sub Workbook_AddinInstall()
Call ShowToolbar
End Sub
'called when add-in uninstalled
Private Sub Workbook_AddinUninstall()
Call DeleteCommandBar
End Sub
On module1
Public Const TOOLBARNAME = "PivotTools"
After moving all function to module1 , then retain caller function on ThisWorkbook the error now gone. Seem that i have to define all functionality that call/ use constant (Public Const TOOLBARNAME = "PivotTools") in the same file (in my case in module1)
on module1 file
Public Const TOOLBARNAME = "PivotTools"
'caller method'
Public Sub protectDoc()
On Error GoTo errorInfo
protectSheet
'if success, show msg box'
MsgBox ("Report berhasil diproteksi")
Exit Sub
errorInfo:
MsgBox Err.Description & vbCrLf & Err.Number
End Sub
Public Sub protectSheet()
ActiveWorkbook.ActiveSheet.protect Password:="password", AllowUsingPivotTables:=True
End Sub
Public Sub refreshDoc()
On Error GoTo errorInfo
refreshConnection
protectSheet
'if success, show msg box'
MsgBox ("Report berhasil diperbaharui")
Exit Sub
errorInfo:
MsgBox Err.Description & vbCrLf & Err.Number
End Sub
Private Sub refreshConnection()
ActiveWorkbook.ActiveSheet.Unprotect Password:="password"
ActiveWorkbook.RefreshAll
End Sub
Public Sub ShowToolbar()
' Assumes toolbar not already loaded '
Application.CommandBars.Add TOOLBARNAME
AddButton "Proteksi Sheet", "Memproteksi Pivot", 225, "protectDoc"
AddButton "Refresh Data", "Refresh Pivot", 459, "refreshDoc"
' call AddButton more times for more buttons '
With Application.CommandBars(TOOLBARNAME)
.Visible = True
.Position = msoBarTop
End With
End Sub
Public Sub AddButton(caption As String, tooltip As String, faceId As Long, methodName As String)
Dim Btn As CommandBarButton
Set Btn = Application.CommandBars(TOOLBARNAME).Controls.Add
With Btn
.Style = msoButtonIcon
.faceId = faceId
' choose from a world of possible images in Excel: see http://www.ozgrid.com/forum/showthread.php?t=39992 '
.OnAction = methodName
.TooltipText = tooltip
End With
End Sub
Public Sub DeleteCommandBar()
Application.CommandBars(TOOLBARNAME).Delete
End Sub
on ThisWorkbook
'called when add-in installed
Private Sub Workbook_AddinInstall()
Call Module1.ShowToolbar
End Sub
'called when add-in uninstalled
Private Sub Workbook_AddinUninstall()
Call Module1.DeleteCommandBar
End Sub
I had this issue, but found that I had my module named the same as my sub (e.g. module named as "InsertLineID" and the sub was "InsertLineID").
Changing the module name to "LineID" and leaving the sub as "InsertLineID" worked a treat for me!