access vba: go to next iteration on error - vba

Listbox2 is populated from items in a table, which itself is populated from listbox1. An error will be thrown if an attempted addition to the table contains duplicate keys. I want my code to handle the error by skipping the problem iteration in question, instead of stopping halfway through the loop.
My code looks something like this:
Public Sub CopySelected(ByRef frm As Form)
Dim ctlSource As Control
Dim intCurrentRow As Integer
Set ctlSource = Me!listbox1
On Error GoTo nonrelation
Dim rst As dao.Recordset
Set rst = CurrentDb.OpenRecordset("Select * from [tempTable]")
For intCurrentRow = 0 To ctlSource.ListCount - 1
If ctlSource.Selected(intCurrentRow) Then
rst.AddNew
rst![field1] = Forms![myForm]![listbox1].Column(1, intCurrentRow)
rst![field2] = Forms![myForm]![listbox1].Column(0, intCurrentRow)
rst.Update
Forms![myForm]!listbox2.Requery
End If
Next intCurrentRow
Forms![myForm]!listbox2.Requery
done:
Exit Sub
nonrelation:
MsgBox Err.Description
End Sub
I know I have to use a 'resume' command somehow in place of my MsgBox Err.Description, but I've never used it. I would like to know how to properly implement that into my code. Thanks!

You could check if the record exists with a helper function and only add if not.
Public Function Exists(ByVal Value As String) As Boolean
Exists = DCount("*","tempTable","[field1]='" & Value & "'") > 0
End Function
Then inside your loop check each record before attempting to insert.
For intCurrentRow = 0 To ctlSource.ListCount - 1
If ctlSource.Selected(intCurrentRow) Then
If Not Exists(Forms![myForm]![listbox1].Column(1, intCurrentRow)) Then
With rst
.AddNew
![field1] = Forms![myForm]![listbox1].Column(1, intCurrentRow)
![field2] = Forms![myForm]![listbox1].Column(0, intCurrentRow)
.Update
End With
Forms![myForm]!listbox2.Requery
End If
End If
Next intCurrentRow
Note the above example expects a String. In case of a numeric, you will need to remove the ' ' quotes.

Related

How to check if quantity is more or less on a table?

I need to check if I have enough quantity of a specific product. How do I make it so the quantity that it checks is the one of the product in a textbox(txtIdProd).
This is my code:
Private Sub Cantidad_LostFocus()
Set myDatabase = CurrentDb()
Set myRs = myDatabase.OpenRecordset("TblProductos", dbOpenDynaset)
Dim cant As Integer
cant = txtCantidad
myRs.FindFirst "IDProducto=" & Me.txtIdProd
If myRs.NoMatch = False Then
If cant > myRs("CantidadDisponible") Then
Me.Label27.Visible = True
End If
End If
Exit Sub
Me.Label27.Visible = False
End Sub
The FindFirst code should do exactly what you want. However, probably more efficient to filter the recordset.
Private Sub Cantidad_LostFocus()
Dim cant As Integer
If Not IsNull(Me.txtIDProd) Then
Set myDatabase = CurrentDb()
Set myRs = myDatabase.OpenRecordset("SELECT CantidadDisponible FROM TblProductos WHERE IDProducto=" & Me.txtIdProd, dbOpenDynaset)
cant = rs!CantidadDisponible
End If
Me.Label27.Visible = Me.txtCantidad <= cant
End Sub
Another approach that doesn't use recordset:
Me.Label27.Visible = Me.txtCantidad <= Nz(DLookup("CantidadDisponible", "TblProductos", "IDproducto=" & Nz(Me.txtIDProd,0)), 0)
Note use of <= and assumes txtCantidad will not have 0.

Passing Values in VBA

In the code I am posting, I am using a check box called "ACDS Test" and whenever it is checked it creates a sheet, then when it becomes unchecked it calls the upper function and deletes the sheet.
I am trying to add a message box that essentially works like a fail safe to ensure they want to delete the page. If they say they do not want to delete the page then I want the checkbox to stay checked.
For some reason I am getting this error message when I try to pass the value to make sure the checkbox stays checked and I cannot figure out why.
The error comes up on the line:
Sub ACDSTest_Click(CorrectValue As Integer)
And the specific error is: "Compile error: Procedure Declaration does not match description of event or procedure having the same name".
Any help is much appreciated! IF any more clarification is needed please feel free to ask!
Sub DeleteWorksheet(NameSheet As String)
Dim Ans As Long
Dim t As String
Dim CorrectValue As Integer
Dim i As Long, k As Long
k = Sheets.Count
Ans = MsgBox("Would you like to take this test off of the form?", vbYesNo)
Select Case Ans
Case vbYes
'Code reads through each page and finds one with corresponding name to string t
'Once it finds the correct page, it deletes it
For i = k To 1 Step -1
t = Sheets(i).Name
If t = NameSheet Then
Sheets(i).Delete
End If
Next i
CorrectValue = 0
Case vbNo
CorrectValue = 1
End Select
End Sub
Sub ACDSTest_Click(CorrectValue As Integer)
Dim NameSheet As String
Dim NameValue As String
NameSheet = "ACDS"
NameValue = "ACDS Test"
If ACDSTest.Value = True Then
CreateWorksheet (NameSheet), (NameValue)
Worksheets("Sheet1").Activate
Else
DeleteWorksheet (NameSheet)
If CorrectValue = 1 Then
ActiveSheet.Shapes("ACDS Test").ControlFormat.Value = 1
End If
End If
End Sub
The issue here is that the CorrectValue variable as you define it in DeleteWorksheet does not exist in the context of the
variable does not exist in context of the ACDSTest_Click subroutine. This is because variables defined within subroutines or functions are local to those functions. To correct this I would convert DeleteWorksheet to a function such as the below.
Further, the event that fires Private Sub ACDSTest_Click() cannot handle passing a value to that function, so changing it to Sub ACDSTest_Click(CorrectValue As Integer) causes an error.
Function DeleteWorksheet(ByVal SheetName As String) As Boolean
On Error GoTo SheetDNE
SheetName = Sheets(SheetName).Name 'Check if sheet exists w/o other objects
On Error GoTo 0
Select Case MsgBox("Would you like to take this test off of the form?", vbYesNo)
Case vbYes
Application.DisplayAlerts = False
Sheets(SheetName).Delete
Application.DisplayAlerts = True
DeleteWorksheet = True
Case Else: DeleteWorksheet = False
End Select
Exit Function 'Exit The Function w/o error
SheetDNE: 'Sheet Does Not Exist
MsgBox "The indicated sheet, " & SheetName & ", does not exist", vbOKOnly
End Function
And
Private Sub ACDSTest_Click()
Dim NameSheet As String
Dim NameValue As String
NameSheet = "ACDS"
NameValue = "ACDS Test"
If ACDSTest.Value = True Then
CreateWorksheet (NameSheet), (NameValue)
Worksheets("Sheet1").Activate
Else
If Not DeleteWorksheet(NameSheet) Then _
ActiveSheet.Shapes("ACDS Test").ControlFormat.Value = 1
End If
End Sub

Type mismatch error in VBA when adding data to textbox

I have a TextBox and a ListBox with a list of various cities being populated from an Excel file
Now each city has one of two options: either within territory or outside. I want that option to be shown in textBox
I tried something like this :
Private Sub CommandButton1_Click()
TextBox2.Value = Application.VLookup(Me.ListBox1.Text,Sheets("Sheet1").Range("B:C"), 2, False)
End Sub
But am getting error stating that :
Run Time Error 2147352571 (80020005) . Could not set Value property. Type mismatch.
My excel file is something like this :
Let say your data are stored in Sheet1. You want to bind these data to ListBox1 on UserForm. I'd suggest to use custom function to load data instead of binding data via using RowSource property. In this case i'd suggest to use Dictionary to avoid duplicates.
See:
Private Sub UserForm_Initialize()
Dim d As Dictionary
Dim aKey As Variant
Set d = GetDistinctCitiesAndTerritories
For Each aKey In d.Keys
With Me.ListBox1
.AddItem ""
.Column(0, .ListCount - 1) = aKey
.Column(1, .ListCount - 1) = d.Item(aKey)
End With
Next
End Sub
'needs reference to Microsoft Scripting Runtime!
Function GetDistinctCitiesAndTerritories() As Dictionary
Dim wsh As Worksheet
Dim dict As Dictionary
Dim i As Integer
Set wsh = ThisWorkbook.Worksheets("Sheet1")
Set dict = New Dictionary
i = 2
Do While wsh.Range("A" & i) <> ""
If Not dict.Exists(wsh.Range("B" & i)) Then dict.Add wsh.Range("B" & i), wsh.Range("C" & i)
i = i + 1
Loop
Set GetDistinctCitiesAndTerritories = dict
End Function
After that, when user clicks on ListBox, city and territory are displayed in corresponding textboxes.
Private Sub ListBox1_Click()
Me.TextBoxCity = Me.ListBox1.List(Me.ListBox1.ListIndex, 0)
Me.TextBoxTerritory = Me.ListBox1.List(Me.ListBox1.ListIndex, 1)
End Sub
Note: code was written straight from the head, so it can contains errors!
The problem is likely that you aren't checking to see to see if the call to Application.VLookup succeeded. Most values returned can be successfully cast to a String - with one important exception: If the VLookup returns an error, for example it doesn't find Me.ListBox1.Text - it can't cast the Variant returned directly.
This should demonstrate:
Private Sub ReturnsOfVLookup()
Dim works As Variant, doesnt As String
works = Application.VLookup("Something not found", _
Sheets("Sheet1").Range("B:C"), 2, False)
Debug.Print works
On Error Resume Next
doesnt = Application.VLookup("Something not found", _
Sheets("Sheet1").Range("B:C"), 2, False)
If Err.Number <> 0 Then
Debug.Print Err.Description
Else
Debug.Print doesnt 'We won't be going here... ;-)
End If
End Sub

Why ListBox doesn't have a FindString method in Excel-VBA?

Trying to search on a ListBox. Specifically, I want to look at an array of items from the Cell, and for each one that matches an entry in the ListBox, I want it to select that List.
I copy-pasted some code that was supposed to let me find a string, but it keeps telling me:
Compile Error: Method or Data Member not found.
Any suggestions?
Relevant Code:
Public local_Target As Range
' local_Target is assigned in the sheet function to pass it here
Private Sub Network_ListBox_Enter()
' Get data in cell (if any)
Dim current_data As String
Dim entries() As String
current_data = local_Target.Value
If current_data = "" Then
Exit Sub
Else
entries = Split(current_data, vbNewLine)
End If
For Each Item In entries
FindMyString Item
Next Item
End Sub
Private Sub UserForm_Terminate()
Dim index As Integer
Dim result As String
' Iterate through the listbox and create the result, then assign to
' Target.value
For index = 0 To Network_ListBox.ListCount - 1
If Network_ListBox.Selected(index) Then
' stuff
If result = "" Then
result = Network_ListBox.List(index)
' ElseIf index = Network_ListBox.ListCount - 1 Then
' result = result + Network_ListBox.List(index)
Else
result = result + vbNewLine + Network_ListBox.List(index)
End If
End If
Next index
local_Target.Value = result
End Sub
Sub FindMyString(ByVal searchString As String)
' Ensure we have a proper string to search for.
If searchString <> "" Then
' Find the item in the list and store the index to the item.
Dim index As Integer
index = Me.Network_ListBox.FindString(searchString)
' Determine if a valid index is returned. Select the item if it is valid.
If index <> -1 Then
Network_ListBox.SetSelected index, True
'Else
' MessageBox.Show ("The search string did not match any items in the ListBox")
End If
End If
End Sub
I checked Intellisense and I don't think that Method is supported in VBA. Other documentations I've found refers to .Net Framework only as well. So maybe, it is not really supported in VBA, but regardless, you can create a function to do just that. Something like below.
Private Function SearchString(mysearch As String, mylist As Variant) As Long
Dim itm As Variant, idx As Long: idx = 0
If IsArray(mylist) Then
For Each itm In mylist
If mysearch = itm Then
SearchString = idx: Exit Function
End If
idx = idx + 1
Next
End If
SearchString = -1
End Function
And you can use it like this:
Private Sub CommandButton1_Click()
Dim i As Long
'do the search
i = SearchString("WhatImSearching", Me.ListBox1.List)
'select the item that match your search
If i <> -1 Then Me.ListBox1.Selected(i) = True
End Sub
I'm not saying that the function I created above is the most efficient way.
That is just an example to give you an idea for a workaround. HTH.
Important: This works in single column ListBox which have a 1D array list. If you need to work on multi-column ListBox, you'll have to tweak the function a little.

Why does Microsoft Visual Basic skip over part of this code

I'm trying to make some labels on my Form to be visible, but i don't want to use a lot of if statements, but for some reason whenever i put Me.Controls(lbl).Visbel = True in a for or do loop it skips the whole loop. The code worked perfectly the way I wanted it until i got an error for calling Dim lbl = Controls("Label" & counter_3) for the whole class instead of in the From_load private sub. Sometimes i can get it to work, but only one label is visible
Dim chararray() As Char = word_list(random_word).ToCharArray
Dim lbl = "Label" & counter_3
For Each item In chararray
If item = Nothing Then
Else
word_list(counter_2) = item.ToString()
counter_2 += 1
End If
Next
For Each item In chararray
If item = Nothing Then
Else
counter_3 += 1
Me.Controls(lbl).Visible = True
MsgBox(item & " " & counter_3)
End If
Next
I've also tried. In both the loops are completely skipped over. I know this because the MsgBox's don't appear.
Dim chararray() As Char = word_list(random_word).ToCharArray
Dim lbl = Controls("Label" & counter_3)
For Each item In chararray
If item = Nothing Then
Else
word_list(counter_2) = item.ToString()
counter_2 += 1
End If
Next
For Each item In chararray
If item = Nothing Then
Else
counter_3 += 1
lbl.Visble = True
MsgBox(item & " " & counter_3)
End If
Next
The thing that I am noticing is that you are creating a Char array based on a random word returned from your word_list, you then iterate through the Char array using the count of the character in the array as an index into your word_list, if the amount of characters in your word exceeds the amount of words in your list you will get an error and since this error is in the Forms Load event it will be swallowed and all the code after it will be aborted. There are also other issues that I would change like making sure all declarations have a type and I would probably use the Controls.Find Method instead and check that it has an actual object. But what I would probably do first is move your code to a separate Subroutine and call it after your IntializeComponent call in the Forms Constructor(New) Method.
Something like this.
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
YourMethod
End Sub
Public Sub YourMethod()
Dim chararray() As Char = word_list(random_word).ToCharArray
Dim lbl As Control() = Controls.Find("Label" & counter_3, True)
For Each item In chararray
If item = Nothing Then
Else
word_list(counter_2) = item.ToString()
counter_2 += 1
End If
Next
For Each item In chararray
If item = Nothing Then
Else
counter_3 += 1
If lbl.Length > 0 Then
lbl(0).Visible = True
Else
MsgBox("Control not Found")
End If
MsgBox(item & " " & counter_3)
End If
Next
End Sub