I'm trying to pass a listbox to another sub to populate it, this is designed to allow multiple listboxes to use the same code by passing the name of the listbox, and the name of the table (which checks whether the input is valid) to the sub. At the moment the me.Autoclavelist (which is the name of the listbox on this particular form) in the below code displays 'null' when you hover over it.
1) What is the listbox object actually expecting as input?
2) Subs 1 and 2 appear to work even with the null being sent - however similar code using a .removeitem doesn't (3 & 4) why is this?
Thanks!
Edit: I found the issue here - I had locked the listboxes on the forms. This meant that it wasn't possible to select any items in them, and the value was therefore always null, so the second two subs failed.
Sub 1:
Private Sub cmdAutoClaveAddItem_Click()
On Error GoTo Errorhandler
Call AddtoList(Me.AutoClaveList, "[Autoclave Process]")
If Me.AutoClaveList.ListCount <> 0 Then
Me.cmdRunAutoclave.Enabled = True
Me.CmdRemoveItem.Enabled = True
End If
SubExit:
Exit Sub
Errorhandler:
MsgBox Error$
Resume SubExit
End Sub
Sub 2:
Private Sub AddtoList(ListName As Listbox, FormName As String)
On Error GoTo Errorhandler
Dim StrLabel_Id As String
Dim l As Long
ListName.RowSourceType = "Value List"
StrLabel_Id = InputBox("Scan Tray Label", "Scan")
If StrLabel_Id = "" Then
GoTo SubExit
Else
If Not IsNull(DLookup("[Tracking_Label_ID]", "Label_Production", "[Tracking_Label_ID]='" & StrLabel_Id & "'")) Then
If IsNull(DLookup("[Tracking_Label_ID]", FormName, "[Tracking_Label_ID]='" & StrLabel_Id & "'")) Then
l = 0
For l = 0 To (ListName.ListCount - 1) Step 1
If ListName.ItemData(l) = StrLabel_Id Then
Call MsgBox("Label is already in batch!", , "Error")
GoTo SubExit
End If
Next
ListName.AddItem StrLabel_Id
Else
Call MsgBox("Label has already been processed", , "Error")
End If
Else
Call MsgBox("Label does not exist. Make sure you create label in Label Production", , "Error")
End If
End If
SubExit:
Exit Sub
Errorhandler:
MsgBox Error$
Resume SubExit
End Sub
Sub 3:
Private Sub CmdRemoveItem_Click()
On Error GoTo Errorhandler
Call RemovelistItem(AutoClaveList)
SubExit:
Exit Sub
Errorhandler:
MsgBox Error$
Resume SubExit
End Sub
Sub 4:
Private Sub RemovelistItem(ListName As Listbox)
On Error GoTo Errorhandler
Dim strRemoveItem As String
If Not IsNull(ListName.Value) Then
strRemoveItem = ListName.Value
ListName.RemoveItem (strRemoveItem)
Else
GoTo SubExit
End If
SubExit:
ListName.Requery
Exit Sub
Errorhandler:
MsgBox Error$
Resume SubExit
End Sub
Don't worry about the Null. The object exists and is just fine. The default property of ListBox objects is Value; therefore the value of Value is what gets displayed when you mouse over Me.AutoClaveList. Its Value happens to be Null (which it is by default):
Null Indicates the item is in a null state, neither selected nor cleared.
For more info, you can have a look at ListName and its properties in the Locals window.
Of course, if you do this:
If Not IsNull(ListName.Value) Then
'do stuff
Else
GoTo SubExit
End If
then it "won't work" i.e. will not do anything because .Value is Null. Get rid of that condition.
Related
I have a user form which updates around 650 Content Controls in the Word Document. It works fine when the Track Changes function is disabled. When the user would like to Track Changes they are getting an error message 5097 in SubmitEndorsement function. Content Control labels are not locked in the file.
Private Sub b_Submit_Click()
On Error GoTo ErrHandler
For Each openDoc In Application.Documents
openDoc.Activate
If ActiveDocument.SelectContentControlsByTag("UniqueDocN").Count > 0 Then
If ActiveDocument.SelectContentControlsByTag("UniqueDocN").Item(1).Range.Text = UniqueDocNumber Then
SubmitEndorsement
End If
End If
Next openDoc
Adios:
Exit Sub
ErrHandler:
MsgBox "Uh oh - an error has occurred with Function UserForm_Submit ."
Resume Adios
Resume
End Sub
Sub SubmitEndorsement()
On Error GoTo ErrHandler
Call updateControl("CCProgramme", "txt_Programme")
Adios:
Exit Sub
ErrHandler:
MsgBox "Uh oh - an error has occurred with Function SubmitEndorsement."
Resume Adios
Resume
End Sub
Sub updateControl(ctl_name As String, txtBox_name As String)
With ActiveDocument
If UserForm.Controls(txtBox_name).value <> .SelectContentControlsByTag(ctl_name).Item(1).Range.Text Then
.SelectContentControlsByTag(ctl_name).Item(1).Range.Text = UserForm.Controls(txtBox_name).value
End If
End With
End Sub
My form allows users to filter by various controls, including a search box for strings. A separate function, CalculateSearchString, processes this search field for filtering (keywords, exact phrases etc); and within this function I use error handling to trap errors caused by incorrect user input (i.e. mucking up the punctuation). The error handling works without a hitch, but I would like the code to come to a complete stop if the search input is incorrect.
What I want:
user inputs incorrectly formatted search string, clicks on the filter
function CalculateSearchFilter throws error. Message box: "fix your search terms!"
code stops completely, and filter is not applied
What actually happens:
user inputs incorrectly formatted search string, clicks on the filter
function CalculateSearchFilter throws error. Message box: "fix your search terms!" Exit function
calling procedure cmdFilterOn still runs, applying an incomplete filter (as though the search box had been empty)
Question: How do I halt code execution completely, not just in the function but in its calling procedure(s)? The function is used in more than one place, so merging it with the calling procedure is not practical.
Private Sub cmdFilterOn()
Dim strSearch As String
strSearch = CalculateSearchFilter
'do more stuff
End Sub
Private Function CalculateSearchFilter() As String
On Error GoTo ErrHandler
'do stuff
If 'user input is wrong, then raise a custom error:
Err.Raise 50000
End If
ExitHandler:
Exit Function
ErrHandler:
If Err.Number = 50000 Then msgbox "Fix your search terms!"
Resume ExitHandler
End Sub
Private Sub cmdYetAnotherButton()
'which also calls on CalculateSearchFilter
End Sub
Moving the input validation to the calling procedure isn't practical either, as it would force me to repeat much of the code in CalculateSearchFilter().
(As I was writing this another solution occurred to me, which is to set CalculateSearchFilter = "an error occurred" in the function error handler, and in the calling procedure
If CalculateSearchFilter = "an error occurred" Then Exit Sub
...but is there a more "official" answer?)
You could try to return a boolean value instead
Option Explicit
Private Sub cmdFilterOn()
Dim strSearch As String
If CalculateSearchFilter(strSearch) Then
'do more stuff
End If
End Sub
Private Function CalculateSearchFilter(ByRef strSearch As String) As Boolean
On Error GoTo ErrHandler
'do stuff
If 'user input is wrong, then raise a custom error:
Err.Raise 50000
End If
CalculateSearchFilter = True
ExitHandler:
Exit Function
ErrHandler:
If Err.Number = 50000 Then MsgBox "Fix your search terms!"
CalculateSearchFilter = False
Resume ExitHandler
End Function
Private Sub cmdYetAnotherButton()
'which also calls on CalculateSearchFilter
End Sub
Alternative:
As mentioned in the comments if you would like to get a string back then I would return an empty string. I would not recommend to return a string like an error occured or whatsoever
Option Explicit
Private Sub cmdFilterOn()
Dim strSearch As String
strSearch = CalculateSearchFilter
If Len(strSearch) > 0 Then
'do more stuff
End If
End Sub
Private Function CalculateSearchFilter() As String
On Error GoTo ErrHandler
'do stuff
If 'user input is wrong, then raise a custom error:
Err.Raise 50000
End If
ExitHandler:
Exit Function
ErrHandler:
If Err.Number = 50000 Then MsgBox "Fix your search terms!"
CalculateSearchFilter = vbNullString
Resume ExitHandler
End Function
Private Sub cmdYetAnotherButton()
'which also calls on CalculateSearchFilter
End Sub
The code I currently works as follows: I type in a UID and then a message box shows the slack of a task. However, it's not possible to edit the Microsoft Project file while the message box is open.
Is there another object I can use in VBA to show the same output but allow me to work on the project file while having the output out? And, is it possible to have the output be in real time? In other words, if I make changes in my schedule, can I see the output constantly change if the slack changes as I make changes without having to run the application again?
Sub SlackFinder()
Dim User_UID, User_ID As Integer
Dim Slack As Variant
Dim NewSlack As Variant
User_UID = InputBox("Enter UID for slack:")
If User_UID = "" Then Exit Sub
On Error GoTo Error_Not_Found
User_ID = ActiveProject.Tasks.UniqueID(User_UID).ID
On Error GoTo Error_Collapsed
Slack = ActiveProject.Tasks.UniqueID(User_UID).TotalSlack
NewSlack = Slack / 480
MsgBox "Total Slack: " & NewSlack
Exit Sub
Error_Not_Found:
MsgBox "UID " & User_UID & " not found in " & ActiveProject.Name
Exit Sub
Error_Collapsed:
MsgBox "UID is present but cannot be selected. Perhaps it is collapsed?", vbOKOnly, "COLLAPSED UID?"
Exit Sub
End Sub
You can show real-time slack using a modeless userform. Create a userform in VBA, for example something that has a textbox for entering the task UID and a label to display the Total Slack value:
Then add this code to the UserForm module:
Private Sub UID_Change()
UpdateTotalSlack
End Sub
Sub UpdateTotalSlack()
On Error Resume Next
Me.TSlack = "Total Slack = " & ActiveProject.Tasks.UniqueID(Me.UID).TotalSlack / 480
End Sub
Add this to the Project module:
Sub ShowSlack()
UserForm1.Show False
End Sub
Private Sub Project_Change(ByVal pj As Project)
UserForm1.UpdateTotalSlack
End Sub
To start, call the ShowSlack procedure. This shows the user form modelessly (e.g. it floats above the MS Project window, allowing you to make changes in the schedule). Enter a Task UID in the textbox and the Total Slack will be displayed immediately and updated whenever changes are made to the schedule (thanks to the Change event code).
Project Module:
Private Sub Project_Change(ByVal pj As Project)
MsgBox "hi"
UserForm10.UpdateTotalSlack
End Sub
Module 29:
Sub ShowSlack()
UserForm10.Show False
End Sub
Userform10:
Dim User_UID As Variant
Dim TSlack As Variant
Private Sub TextBox3_Change()
User_UID = UserForm10.TextBox3.Value
UpdateTotalSlack
End Sub
Sub UpdateTotalSlack()
On Error Resume Next
If Not User_UID = "" Then
TSlack = ActiveProject.Tasks.UniqueID(User_UID).TotalSlack / 480
Else
TSlack = ""
End If
UserForm10.Label1.Caption = TSlack
End Sub
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
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