Object required error when executing function - vba

In my user form I have a textbox and only numeric values are allowed to be input into this textbox, once a numeric value is input and the button next to the texbox is clicked it is supposed to do a calculation on the numeric value and add it to my listbox. When I click the button to add the numeric value I get an error object required. Below shows how I am trying to execute this process
userInput= Textbox1.Value
List.AddItem executeFormula(userInput)
Function executeFormula(inputs As Integer)
inputs = inputs * 5
End Function
I have narrowed down the issue, the function is working perfectly but it is when I am trying to add the function onto the end of the List.AddItem

Have you defined Option Explicit at the top of your code? Is the List object correctly named based on what you have on your form? I suspect this is the old VBA equivalent of a null reference exception and VBA isn't able to get a working reference to that listbox.
You could try using:
Set List = UserForm1.List ' or whatever the name of your form is.

Two small problems:
your function should return its result thru the function name
you should explicitly add a String
starting with a blank "forms-style" listbox:
Sub trewr()
Dim lb As ListBox, ii As Integer
Set lb = ActiveSheet.ListBoxes(1)
ii = 56
lb.AddItem (CStr(executeFormula(ii)))
End Sub
Function executeFormula(inputs As Integer) As Integer
executeFormula = 5 * inputs
End Function

Related

Detect if combobox has been modified in the last Xs [duplicate]

Good morning!
I have a "fancy" search function in Microsoft Access where the list of possible options shrinks as you type in the search field. Unfortunately the computer and server can't keep up with these rapid requeries of the data.
Currently the command to requery with the field in the 'onchange' function of the search box. I'd like to add a delay so it only runs the requery when the search box has not changed for a second. Thus if someone types in a 8 letter word, it isn't running 8 requeries.
The current idea I have for it, which I know there must be something better, is..
"On change, set search box value to X and wait 1 second. After 1 second, if X = search box value, run the requery. An issue is that it would be rapidly rewriting the X value and have a 'wait' command floating for each letter.
Hopefully there's a way to write an event trigger of "When field X has changed, but not changed for the past second."
Thank you!
As requested, here is my current code
'Create a string (text) variable
Dim vSearchString As String
'Populate the string variable with the text entered in the Text Box SearchFor
vSearchString = SearchFor.Text
'Pass the value contained in the string variable to the hidden text box SrchText,
'that is used as the sear4ch criteria for the Query QRY_SearchAll
SrchText = vSearchString
'Requery the List Box to show the latest results for the text entered in Text Box SearchFor
Me.SearchResults.Requery
Me.SearchResults2.Requery
'Tests for a trailing space and exits the sub routine at this point
'so as to preserve the trailing space, which would be lost if focus was shifted from Text Box SearchFor
If Len(Me.SrchText) <> 0 And InStr(Len(SrchText), SrchText, " ", vbTextCompare) Then
'Set the focus on the first item in the list box
Me.SearchResults = Me.SearchResults.ItemData(1)
Me.SearchResults.SetFocus
'Requery the form to refresh the content of any unbound text box that might be feeding off the record source of the List Box
DoCmd.Requery
'Returns the cursor to the the end of the text in Text Box SearchFor,
'and restores trailing space lost when focus is shifted to the list box
Me.SearchFor = vSearchString
Me.SearchFor.SetFocus
Me.SearchFor.SelStart = Me.SearchFor.SelLength
Exit Sub
End If
'Set the focus on the first item in the list box
' Me.SearchResults = Me.SearchResults.ItemData(1)
Me.SearchResults.SetFocus
'Requery the form to refresh the content of any unbound text box that might be feeding off the record source of the List Box
DoCmd.Requery
'Returns the cursor to the the end of the text in Text Box SearchFor
Me.SearchFor.SetFocus
If Not IsNull(Len(Me.SearchFor)) Then
Me.SearchFor.SelStart = Len(Me.SearchFor)
End If
Obviously this is not MY code, it's from somewhere on the interweb. It works fantastic for databases stored locally, but everything is moving to our Sharepoint server which is running on a 386 in a moldy basement powered by a narcoleptic gerbil.
You can simply use the Timer of the current form. No need for a separate form or anything.
Private Sub DoSearch()
' Your current code
' but you should look into removing as many "Requery" from there as possible!
End Sub
Private Sub SearchFor_Change()
' Wait for x Milliseconds until the search is started.
' Each new change restarts the timer interval.
' Use 1000 (1 s) for slow typists or a really slow server
' 200 ms feels right for a normal typist
Me.TimerInterval = 200
End Sub
Private Sub Form_Timer()
' Disable timer (will be enabled by the next SearchFor_Change)
Me.TimerInterval = 0
' Now run the search
DoSearch
End Sub
Note: you may need to move some of the cursor-handling code from DoSearch() to SearchFor_Change(), specifically:
Me.SearchFor.SelStart = Len(Me.SearchFor)
Assign a shortcut key like (Ctrl+ J) to the logic in on change event and call it on demand once you have finished typing search keyword.
Remove on change event.
Create other procedure which has the logic of on change event and assign a shortcut key
Press shortcut to get search suggestion
Other approach
Add below validation to Change event which will check for length of string and will trigger only if length of string is >=8
Private Sub txtSearch_Change()
If Len(Nz(txtSearch.Text, 0)) >= 8 Then
End If
End Sub
I'm going a little outside my comfort area, since I hardly use MS Access forms, but why are you bothering the Server/Database so much? In my experience, each query costs the same amount of time, whether it returns 1 record or 100,000 records.
So even before the user types anything, why don't you just do a single query to return a sorted list. After that, it takes almost no time to use VBA to process the results and find everything in the list that starts with whatever the user types in (it's sorted after all).
Except for the initial load, users who are local to the database or on the other side of the world will experience the same snappy response from your interface.
----------
Like I said, I haven't messed with Access Forms a lot, so this is more of a strict VBA solution. Maybe there is a better way to do it without going outside the Access Forms box that someone could enlighten us with.
You should basically just call LoadItemList when you load the form, or whenever you need to.
Public dbConn As ADODB.Connection
Private ItemList As Variant
Private RecordCount As Long
Sub LoadItemList()
Dim SQL As String
Dim RS As New ADODB.Recordset
SQL = "SELECT T.Name FROM Table T"
Set RS = dbConn.Execute(SQL)
If Not RS.EOF Then
ItemList = RS.GetRows
RecordCount = UBound(ItemList, 2) - LBound(ItemList, 2) + 1
End If
End Sub
Then replace DoCmd.Requery with AddItemtoCombobox SearchResults, SearchFor.Text
Sub AddItemtoCombobox(Control As ComboBox, Filter As String)
Dim Index As Long
Control.Clear
If Not IsEmpty(ItemList) Then
For Index = 0 To RecordCount - 1
If ItemList(Index) Like Filter Then Control.AddItem ItemList(Index)
Next
End If
End Sub
Again, maybe there is a better way that is built into Access...
The technical term that you're looking for is debounce.
What you can do is on your on change event, keep track of the current search string
in terms of pseudocode.
sub onChange()
Form.timerinterval = 0
setSearchString
form.timerinterval = delay
So in terms of the explanation, if your on change is called, disable the timer. Update your search string, then reset the timer to fire after a certain amount of time. The form should be a hidden form that contains the code that you want to execute

Convert String to Textbox Name

I have a 9 Textboxes and I want to get their values with Val(Me.TxtBoxName.Text)).
The Textboxes already all exists and are named. I want to build the names and access their values with
For i = 0 To 2
For j = 0 To 2
getText = "Me." & "eing_" & i & j & ".Text"
Debug.WriteLine(Val(getText))
Next
Next
(This will get me the Textbox Names eing_00, eing_01, eing_02, eing_10, eing_11, eing_12, eing_20, eing_21, eing_22)
But this does not work as getText is a String. How can I convert it so I can access the Textbox properly?
That's not how it works. You don't magically turn a String into an identifier.
Fortunately though, all controls have a Name property and you can use that property to index the Controls collection of the parent to get a reference to a control. When you add a control in the designer, the Name property is the same as the field declared to refer to the control, e.g. if you add a Button to a form and do this:
MessageBox.Show(Button1.Name)
then it will display "Button1".
So, in your case, that means that you would replace this nonsense:
getText = "Me." & "eing_" & i & j & ".Text"
with this:
getText = Controls($"eing_{i}{j}").Text
That assumes that the controls were added directly to the form. If they were added to some other container, e.g. a Panel, then you must use the Controls collection of that container, rather than that of the form.
I think you need to pay attention to the difference between numbers and strings. The Name property of a Control is a String but the control itself is type Control and the inherited type TextBox in this case. We can refer to an item in the ControlsCollection by providing a String which is the Name property of the control.
The .TryParse takes a String and checks if the string can be converted to a the Type you are parsing. In this case Double. It returns True or False and if True it assigns the converted value to the second parameter. It is important to use this because users can enter any old thing in a text box.
I wasn't sure what kind of number you were expecting so I chose Double. Change to a narrower type if you can. If you need to manipulate numbers with arithmetic they cannot be strings (the Text property is a string)
As a side note, you can do several things with a list of numbers, Average, Max, Min, or Sum.
Dim total = numbers.Sum
Turn on Option Strict to help identify type problems.
Private Sub OpCode()
Dim numbers As New List(Of Double)
For i = 0 To 2
For j = 0 To 2
Dim getText = "eing_" & i & j
Dim ctrl = Controls(getText)
Dim d As Double
If Double.TryParse(ctrl.Text, d) Then
numbers.Add(d)
End If
Next
Next
For Each d In numbers
Debug.Print(d.ToString)
Next
End Sub

Multi valued combo box field visible and invisible another field in MS Access 2016

I have a field called country with multi valued combo box. There are 10 values in the combo box. I need another field to be visible when a value from country field is selected otherwise invisible. I tried to use below VBA script but it gives an error as below
Error:
Runtime error 13
types incompatible
I am using the code below for which I get the above error:
Private Sub country_Click()
If country.Value = "11. OTHER" Then
Me.country_txt.Visible = True
Else
Me.country_txt.Visible = False
End If
End Sub
I am beginner to use MS Access. Can anyone please help.
I am able to solve the issue by slightly changing the script. I used the below script and it works fine.
Private Sub country_Click()
If Me.country.Selected(10) Then
Me.country_txt.Visible = True
Else Me.country_txt.Visible = False
End If
End Sub
So here the issue got resolved when the value of the combo box was called as integer. In the list "11. OTHERS" is the 11th value. So Me.country.Selected(10) did the trick.

MS Access: Action "onchange" event after a delay?

Good morning!
I have a "fancy" search function in Microsoft Access where the list of possible options shrinks as you type in the search field. Unfortunately the computer and server can't keep up with these rapid requeries of the data.
Currently the command to requery with the field in the 'onchange' function of the search box. I'd like to add a delay so it only runs the requery when the search box has not changed for a second. Thus if someone types in a 8 letter word, it isn't running 8 requeries.
The current idea I have for it, which I know there must be something better, is..
"On change, set search box value to X and wait 1 second. After 1 second, if X = search box value, run the requery. An issue is that it would be rapidly rewriting the X value and have a 'wait' command floating for each letter.
Hopefully there's a way to write an event trigger of "When field X has changed, but not changed for the past second."
Thank you!
As requested, here is my current code
'Create a string (text) variable
Dim vSearchString As String
'Populate the string variable with the text entered in the Text Box SearchFor
vSearchString = SearchFor.Text
'Pass the value contained in the string variable to the hidden text box SrchText,
'that is used as the sear4ch criteria for the Query QRY_SearchAll
SrchText = vSearchString
'Requery the List Box to show the latest results for the text entered in Text Box SearchFor
Me.SearchResults.Requery
Me.SearchResults2.Requery
'Tests for a trailing space and exits the sub routine at this point
'so as to preserve the trailing space, which would be lost if focus was shifted from Text Box SearchFor
If Len(Me.SrchText) <> 0 And InStr(Len(SrchText), SrchText, " ", vbTextCompare) Then
'Set the focus on the first item in the list box
Me.SearchResults = Me.SearchResults.ItemData(1)
Me.SearchResults.SetFocus
'Requery the form to refresh the content of any unbound text box that might be feeding off the record source of the List Box
DoCmd.Requery
'Returns the cursor to the the end of the text in Text Box SearchFor,
'and restores trailing space lost when focus is shifted to the list box
Me.SearchFor = vSearchString
Me.SearchFor.SetFocus
Me.SearchFor.SelStart = Me.SearchFor.SelLength
Exit Sub
End If
'Set the focus on the first item in the list box
' Me.SearchResults = Me.SearchResults.ItemData(1)
Me.SearchResults.SetFocus
'Requery the form to refresh the content of any unbound text box that might be feeding off the record source of the List Box
DoCmd.Requery
'Returns the cursor to the the end of the text in Text Box SearchFor
Me.SearchFor.SetFocus
If Not IsNull(Len(Me.SearchFor)) Then
Me.SearchFor.SelStart = Len(Me.SearchFor)
End If
Obviously this is not MY code, it's from somewhere on the interweb. It works fantastic for databases stored locally, but everything is moving to our Sharepoint server which is running on a 386 in a moldy basement powered by a narcoleptic gerbil.
You can simply use the Timer of the current form. No need for a separate form or anything.
Private Sub DoSearch()
' Your current code
' but you should look into removing as many "Requery" from there as possible!
End Sub
Private Sub SearchFor_Change()
' Wait for x Milliseconds until the search is started.
' Each new change restarts the timer interval.
' Use 1000 (1 s) for slow typists or a really slow server
' 200 ms feels right for a normal typist
Me.TimerInterval = 200
End Sub
Private Sub Form_Timer()
' Disable timer (will be enabled by the next SearchFor_Change)
Me.TimerInterval = 0
' Now run the search
DoSearch
End Sub
Note: you may need to move some of the cursor-handling code from DoSearch() to SearchFor_Change(), specifically:
Me.SearchFor.SelStart = Len(Me.SearchFor)
Assign a shortcut key like (Ctrl+ J) to the logic in on change event and call it on demand once you have finished typing search keyword.
Remove on change event.
Create other procedure which has the logic of on change event and assign a shortcut key
Press shortcut to get search suggestion
Other approach
Add below validation to Change event which will check for length of string and will trigger only if length of string is >=8
Private Sub txtSearch_Change()
If Len(Nz(txtSearch.Text, 0)) >= 8 Then
End If
End Sub
I'm going a little outside my comfort area, since I hardly use MS Access forms, but why are you bothering the Server/Database so much? In my experience, each query costs the same amount of time, whether it returns 1 record or 100,000 records.
So even before the user types anything, why don't you just do a single query to return a sorted list. After that, it takes almost no time to use VBA to process the results and find everything in the list that starts with whatever the user types in (it's sorted after all).
Except for the initial load, users who are local to the database or on the other side of the world will experience the same snappy response from your interface.
----------
Like I said, I haven't messed with Access Forms a lot, so this is more of a strict VBA solution. Maybe there is a better way to do it without going outside the Access Forms box that someone could enlighten us with.
You should basically just call LoadItemList when you load the form, or whenever you need to.
Public dbConn As ADODB.Connection
Private ItemList As Variant
Private RecordCount As Long
Sub LoadItemList()
Dim SQL As String
Dim RS As New ADODB.Recordset
SQL = "SELECT T.Name FROM Table T"
Set RS = dbConn.Execute(SQL)
If Not RS.EOF Then
ItemList = RS.GetRows
RecordCount = UBound(ItemList, 2) - LBound(ItemList, 2) + 1
End If
End Sub
Then replace DoCmd.Requery with AddItemtoCombobox SearchResults, SearchFor.Text
Sub AddItemtoCombobox(Control As ComboBox, Filter As String)
Dim Index As Long
Control.Clear
If Not IsEmpty(ItemList) Then
For Index = 0 To RecordCount - 1
If ItemList(Index) Like Filter Then Control.AddItem ItemList(Index)
Next
End If
End Sub
Again, maybe there is a better way that is built into Access...
The technical term that you're looking for is debounce.
What you can do is on your on change event, keep track of the current search string
in terms of pseudocode.
sub onChange()
Form.timerinterval = 0
setSearchString
form.timerinterval = delay
So in terms of the explanation, if your on change is called, disable the timer. Update your search string, then reset the timer to fire after a certain amount of time. The form should be a hidden form that contains the code that you want to execute

Access and VBA - default value of field is null?

I'm making a form with Microsoft Access and I'm trying to make an AfterUpdate event procedure for a field. I need to know if the value of that field is the default value (or if it's empy). I read that the default value of a field in VBA is Null so I made this:
Private Sub EB1_10_Val1_AfterUpdate()
If Me.EB1_10_Val1.Value = Null Then
MsgBox "hello"
End If
End Sub
This didn't work so I tried this for when the user updates the value in the field and then erases it (empties the field)
Private Sub EB1_10_Val1_AfterUpdate()
If Me.EB1_10_Val1.Value = Empty Then
MsgBox "hello"
End If
End Sub
The messages never pop up. On the other hand I tried changing the default value of the field to 0 but Its not working. The 0 doesn't appear in the field as default when in Form View.
You can check if the field is empty with this expression:
If IsNull(Me.EB1_10_Val1.Value) Then
In addition to solution provided by mielk, you can use Nz or Iif function to replace Null with default value.
Usage:
myVal = Nz(Me.SomTextBox, 0)