How to correctly set the list property? (Error 381) - vba

I have created a listbox which can be filtered according to keyword in textbox. It works if I do it normally. However, it stopped working when the list is a dependent source. (The listbox value is like INDIRECT() of F1 and I'm trying to filter that INDIRECT list)
I have 3 lists as shown in the image (A, B, D). D is a list of A without duplicates. the listbox("lbxCustomers") in Userform2 uses a dependent rowsource according to word selected at Cell F2. [It works until here]
The values in the listbox will be filtered according to keyword in textbox("tbxFind"). I'm getting an error at this line ".List = vaCustNames". I tried to change it into a normal range (sheet1.range(...)) and it works but the list is not a dependent list of F1.
image
Private Sub UserForm_Initialize()
Me.lbxCustomers.RowSource = Range("F2").Value
End Sub
Private Sub tbxFind_Change()
Dim i As Long
Dim sCrit As String
Dim vaCustNames As Variant
vaCustNames = Range("F2").Value
Debug.Print Range("F2").Value
sCrit = "*" & UCase(Me.tbxFind.Text) & "*"
With Me.lbxCustomers
.RowSource = ""
'Start with a fresh list
.List = vaCustNames
'.List = Sheet1.Range("B2:B13").Value 'tested with range and worked
For i = .ListCount - 1 To 0 Step -1
'Remove the line if it doesn’t match
If Not UCase(.List(i)) Like sCrit Then
.RemoveItem i
End If
Next i
End With
End Sub

Related

Printing a different value in a text box on multiple copies

I have a button that prints a form on the current record.
The form contains a combobox with something like: 123005TEST
This combobox is a lookup to another textbox which is a combination of three text boxes(on a different form):
=([OrderNr] & (""+[Aantal]) & "" & [SapArtNr])
OrderNr is 12300 and Aantal is 5 and SapArtNr is TEST, creating: 123005TEST
My question is, when I click print, is it possible to print a certain amount of copies based on Aantal 5, so printing 5 copies.
And here comes the tricky part.
To have each printed copy a different value in the combobox, so the first copy would have this written in the combobox on the printed paper: 123001TEST and copy two would be 123002TEST and so on, until 5.
I didn't understand which textbox will receive the sequential text. So I put a dummy in the example code:
Option Explicit
Private Sub cmdPrintIt_Click()
Dim strOrderNr As String
Dim strAantal As String
Dim strSapArtNr As String
Dim intHowManyCopies As Integer
Dim intCopy As Integer
Dim strToTextBox As String
strOrderNr = Me.OrderNr.Text
strAantal = Me.Aantal.Text
strSapArtNr = Me.SapArtNr.Text
On Error Resume Next
intHowManyCopies = CInt(strAantal)
On Error GoTo 0
If intHowManyCopies <> 0 Then
For intCopy = 1 To intHowManyCopies
strToTextBox = strOrderNr & CStr(intCopy) & strSapArtNr
Me.TheTextBoxToReceiveText.Text = strToTextBox
'put your code to print here
Next
Else
MsgBox "Nothing to print! Check it."
End If
End Sub

VBA Public Variable Not Working - MS Word - Counting Characters in a Table

The main VBA procedure counts characters in table cells in a Word document. Since it can count characters different ways:
Count the "Objective" text for the selected table
Count the "Accomplishment" text for the selected table
Count both the Obj and Acc texts in each table (loop), for all tables (another loop)
I created calling procedures for each option above that calls the main procedure. This way I pass variables from the calling Sub to the main Sub. These variables (1) tell the main Sub whether I want to count what is in row 3 (objective) or in row 5 (accomplishment) or both, and (2) feed the If/then lines in the main Sub to make sure the right row is counted. At the time, it seemed elegant, in hindsight - not so much.
Word template below:
There will be text in O1 and the VBA will count it (characters, spaces + paragraphs) and output it in C1, and the C1 fill changes red or green if over/under the character limit. The same for A1 and C2 and so on for any number of following tables.
PROBLEM DESCRIPTION
The VBA was working for the actions above when I had the row/columns hard coded into various places in the code. If rows/columns were ever added/deleted from the tables, they would have to updated in multiple spots. It would be simpler if the row/column numbers were in one place and referred back to as variables, so I changed the row/col #s to public variables. Then the problem began.
In the code, I track (debug.print) what becomes of oRow (output row) & chcct (character count col) and both are 0 as the main Sub runs, despite both being initialized as 3 in the public Sub Row_Col_Num() below.
My public variables are at the top of the module before the first Sub() and denoted as Public. Sub Row_Col_Num() which contains the variable assignments is also Public. All Subs are in the same standard module.
Option Explicit
Public oRow As Integer 'row with "Objectives" text
Public aRow As Integer 'row with "Accomplishments" text
Public cOnA As Integer 'column that both obj and accmp text are in
Public cChCt As Integer 'column that the char count is output to
Public Sub Row_Col_Num()
oRow = 3
aRow = 5
cOnA = 1
cChCt = 3
Debug.Print "cchct pub sub: " & cChCt
End Sub
ATTEMPTS TO FIX PROBLEM & RESULTS
I used the variable normally and left it Public as well as the Sub that assigns the variables (oRow =3) values.
Sub TableCharCount_Obj()
'Run character count for the "Objectives" in the SELECTED table
Debug.Print "orow = " & oRow
Call TableCharCount(oRow, oRow) 'provide it 2x to make IF and FOR loop
End Sub
I tried putting the Sub() name in front of the variable when it is used, e.g. Row_Col_Num.orow, in the Sub above.
Call TableCharCount(Row_Col_Num.oRow, Row_Col_Num.oRow)
I tried the module name in front of the variable as well, e.g. Module1.orow.
Call TableCharCount(Module1.oRow, Module1.oRow)
RESULTS
#1 & #3 resulted in the macro counting the wrong row and outputting to the wrong cell.
#2 resulted in error "Expected Function or variable" at line: Call TableCharCount(Row_Col_Num.oRow, Row_Col_Num.oRow)
All 3 cases orow and cchct both continued to be 0 throughout the run.
QUESTIONS / SOLUTIONS
a) Can a Public variable (oRow) be used as an argument passed from calling Sub to called Sub as ByVal a As Integer?
b) Does Public Sub Row_Col_Num(), which assigns values to the public variables, have to be explicitly run or called to populate the variables in the other Subs w/ the correct values?
c) Should I call Public Sub Row_Col_Num() in every calling Sub before calling the main Sub?
Sub TableCharCount_Obj()
Call Public Sub Row_Col_Num() '<<< add this call
Call TableCharCount(oRow, oRow) 'provide it 2x to make IF and FOR loop
End Sub
This option seems like a bad design.
If it's not obvious, there was some mission creep as I added more capability For now, if I could get the public variables to work, it would be done. Appreciate any suggestion to get these variables to work. For the purposes of this question, I only left the code for the variable Sub, the first calling Sub and the main Sub. VBA below:
'#0 -- This creates variables for column and row number used in all the macros. Only need to change row/col number here if row/col are added/deleted
Option Explicit
Public oRow As Integer 'row with "Objectives" text
Public aRow As Integer 'row with "Accomplishments" text
Public cOnA As Integer 'column that both obj and accmp text are in
Public cChCt As Integer 'column that the char count is output to
'This assigns row/column numbers to the variables
Public Sub Row_Col_Num()
oRow = 3
aRow = 5
cOnA = 1
cChCt = 3
Debug.Print "cchct pub sub: " & cChCt End Sub
'#2
Sub TableCharCount_Obj() 'Run character count for the "Objectives" in the SELECTED table
Debug.Print "orow = " & oRow
Call TableCharCount(oRow, oRow) 'provide it 2x to make IF and FOR loop
End Sub
'other calling procedures removed
'#5
Option Explicit
Sub TableCharCount(ByVal a As Integer, ByVal b As Integer)
'Counts total characters in a cell w/in a table and outputs the number to a different cell, and colors the cell red or green if over/under the maximum number of characters.
Dim charCount, charWSCount, paraCount, charTot As Double
Dim iRng, oRng, txtRng As Word.Range
Dim i, max, s, t, x As Integer
Dim tcount, tbl As Integer
Dim DocT As Table 'for active doc tables
Debug.Print "cchct1= " & cChCt 'Debug.Print vbCr & "-----START-------" & vbCr Application.ScreenUpdating = False
If a <> b Then
tcount = ActiveDocument.Tables.Count
tbl = 1 'used in FOR loop, start w/ table #1
s = b - a '"STEP" used in FOR loop = # of rows between objectives text and accomplishments text Else
On Error GoTo ErrMsg 'handles expected user error of not selecting a table to execute on
tbl = ActiveDocument.Range(0, Selection.Tables(1).Range.End).Tables.Count 'ID the table that is selected
tcount = tbl 'prevents FOR loop from trying to run again
s = 1 '"STEP" used in FOR loop = # of rows between objectives text and accomplishments text / do not set to zero = infinite loop End If
'Debug.Print "# of Tables: " & tcount
For t = tbl To tcount 'loops thru the tables
Set DocT = ActiveDocument.Tables(t)
For x = a To b Step s 'loops thru the applicable row(s) in the table
'Debug.Print "x # start = " & x
'Debug.Print "table " & t
iRng = DocT.Cell(x, cOnA)
iRng.Select
'Count used in output
Selection.MoveLeft wdCharacter, 1, wdExtend 'computerstats requires the text itself selected, characters.count can use the whole cell selected
charWSCount = Selection.Range.ComputeStatistics(Statistic:=wdStatisticCharactersWithSpaces) 'counts bullets & space after bullet / not line breaks (paragraphs)
'Debug.Print "Comp statchar# " & charWSCount
'---------
paraCount = Selection.Range.ComputeStatistics(Statistic:=wdStatisticParagraphs)
'Debug.Print "#paras = " & paraCount
'----------
charTot = charWSCount + paraCount
'Output to table cell
i = x - 1 'output cell is 1 row above cell that is counted
Set oRng = DocT.Cell(i, cChCt).Range 'Char count ouput row,column
Debug.Print "cchct2= " & cChCt
oRng.Text = charTot
Set txtRng = DocT.Cell(i, cChCt - 1).Range '"# Char:" location row,column
txtRng.Text = "# Char:"
'Maximum # of char allowed in a cell. Used to change cell fill red or green.
max = 2000 '"Accomplishment" row (row 5) has a max of 2000
If i = 2 Then max = 1500 '"Objective" row (row 3) has a max of 1500
'Change color of cell to indicate over/under max # of characters
If charCount < max Then
oRng.Shading.BackgroundPatternColor = wdColorBrightGreen
Else: oRng.Shading.BackgroundPatternColor = wdColorRed
End If
'Debug.Print "x # end = " & x
'Debug.Print "--------Next x--------------"
Next x
'Debug.Print "------Next Table------"
Next t
ActiveDocument.Tables(tbl).Select 'attempt to move to top of 1st table if using CharCount_AllTab() or just to the top of the selected table for the other macros
Selection.GoTo What:=wdGoToBookmark, Name:="\Page" Selection.StartOf
Application.ScreenUpdating = True
Exit Sub
ErrMsg: Msgbox "Select a table by placing the cursor anywhere in the table. Press OK and try the macro again numnuts!", _
vbOKOnly, "Table not selected"
End Sub

MS Access: Multi ListBox get values

I have a listbox on a form that contains 6 values. The user is allowed to select multiple values at once. For each value selected I want to assign this value to a variable.
My listbox is called: lstFilterUnits
The listbox contents are as follows:
Blue, Red, Green, Yellow, Orange, White
I understand that the following is associated with each colour:
Forms("Form1").lstFilterUnits.Selected(0) 'Blue Selected
Forms("Form1").lstFilterUnits.Selected(1) 'Red Selected
Forms("Form1").lstFilterUnits.Selected(2) 'Green Selected
Forms("Form1").lstFilterUnits.Selected(3) 'Yellow Selected
Forms("Form1").lstFilterUnits.Selected(4) 'Orange Selected
Forms("Form1").lstFilterUnits.Selected(5) 'White Selected
I have attempted to do this using the below code in the listbox on click event:
As you can see I had to hard code the value assigned to the vUnit variable as anytime I try get the value associated with the list box with either of the lines commented out it doesn't work.
Dim vUnit As String
If Forms("Form1").lstFilterUnits.Selected(0) = True Then
vUnit = "Blue
MsgBox (vUnit)
'MsgBox "You selected " & lstFilterUnits.Value 'Returns null value
'MsgBox "You selected " & lstFilterBusinessUnits.SelectedValue 'Returns Compile error
ElseIf Forms("Form1").lstFilterUnits.Selected(1) = True Then
vUnit = vUnit & ", Red'"
MsgBox (vUnit)
End If
So for the above code I can't seem to assign the value associated with the selected item to a variable. I also want to me able to display multiple values in the variable if they are selected, however if I select more than one value using the above method it will only go into the first if statement that is true, due to this I think I should be using some sort of a loop to assign the variable multiple values but I'm new to VBA and d not know how to do this.
Any help would be appreciated, I think this is probably a simple task that I am overthinking.
EDIT:
After searching online I finally found something that is kind of doing what I need. However the output is also outputting the index number of the listbox. The code I found is below. I've tried playing around with it but can't seem to achieve my desired output.
The below code will output the following:
'1 , 'Blue', '2 'Red', 3 ,'Green',
Whereas I want it to output:
'Blue','Red', 'Green',
Does anyone know how I can achieve this? I've tried changing the column to both only i and J and this didn't seem to work. I've never worked with arrays in VBA before so will admit I don't understand the code fully and the form I found it on didn't explain the code, it was just providing a solution for another user.
Dim i As Long
Dim J As Long
Dim Msg As String
Dim arrItems() As String
ReDim arrItems(0 To lstFilterUnits.ColumnCount - 1)
For J = 0 To lstFilterUnits.ListCount - 1
If lstFilterUnits.Selected(J) Then
For i = 0 To lstFilterUnits.ColumnCount - 1
arrItems(i) = lstFilterUnits.Column(i, J)
Next i
Msg = Msg & "'" & Join(arrItems, " , '") & "', "
End If
Next J
MsgBox Msg
Use the .ItemsSelected collection. It already has all the items.
EXAMPLE:
Sub Example()
dim ctl as Control
Set ctl = Forms("Form1").lstFilterUnits
Dim varItm As Variant
For Each varItm In ctl.ItemsSelected
Debug.Print ctl.ItemData(varItm)
Next varItm
End Sub
SOURCE: https://learn.microsoft.com/en-us/office/vba/api/access.listbox.itemsselected
Have a try on it.
Private Sub lstFilterUnits_Click()
Dim ctl As Control
Dim varItm As Variant
Dim SelectedColor As String
Set ctl = Forms!Form1!lstFilterUnits
For Each varItm In ctl.ItemsSelected
SelectedColor = SelectedColor & vbCrLf & ctl.ItemData(varItm) 'This for each color in each line
'SelectedColor = SelectedColor & "," & ctl.ItemData(varItm) ' This line for comma separated color
Next varItm
MsgBox SelectedColor
End Sub

Remove value from range populating my listbox

Hello everyone I have two listboxes. The first listbox contains all the items to choose from. After selecting an item, a user clicks on an 'ADD' command button to copy that value onto the range of the second listbox. I believe most of you have seen similar add/remove listboxes.
Both listboxes were created by inserting controls and they are populated by an input range of items on a hidden worksheet.
Here is my problem: adding names is works fine, however the 'remove' procedure I created seems to take a long time to complete since the list of items can be more than 200 items.
I use the following code to match a selected listbox value with the input range value and then it clears the contents of the cell in the input range:
Sub remove()
Dim r As Long
Dim al As ListBox
Dim d As Range
Dim dd As Range
Dim allpick As Worksheet
Set al = Worksheets("LISTBOX").ListBoxes("listselected")
Set allpick = Worksheets("columns")
Set dd = allpick.Range("selectedNAMES")
With al
For r = 1 To .ListCount
If .selected(r) Then
For Each d In dd
If d.Value = .List(r) Then
d.ClearContents
End If
Next d
End If
Next r
End With
End Sub
Is there an alternative code or structure I could use so that it doesn't take so long to complete?
I used the find function stated by commenters and wrote the code below. It is much faster and is exactly what I wanted. However, I didn't know what to put after "IF CELL IS NOTHING THEN" so i just used calculate. Any suggestions?
Dim r As Long
Dim al As ListBox
Dim strNAME As String
Dim names As Worksheet
Set names = Worksheets("names")
Set al = Worksheets("HOME").ListBoxes("selectednames")
With al
For r = 1 To .ListCount
If .Selected(r) Then
strNAME = .List(r)
Set cell = names.Range("currentnames").Find(What:=strNAME)
If cell Is Nothing Then
Calculate
Else: cell.ClearContents
End If
End If
Next r
End With

Knowing the assigned name of a cell instead of the "A1" name

Context:
I have several lists in my sheet (1 column wide, 1-10 rows long). When I right click a cell in these lists, I can do several options, that all work well. I have given a name to the cell at the top of each of these lists (ex. Cell A1 has been given the name cell_1, B10 is names cell_2, etc).
I would like to know if the cell I am right clicking on is the one at the top of the list; is it named "cell_(number)"? If it is not, it checks the cell on top of that one. Does it have a name that starts with "cell_"? If not, check the one on top, etc. Until I can figure out the user clicked on an element of WHICH list.
TL;DR The actual question
I can use ActiveCell.Address, which gives me something like "A1" whether or not I have assigned a name to that cell. ActiveCell.Name gives "Sheet1!A1", so it's not much better. Any idea how to get it to return the name I have assigned instead?
Create a UDF to test the application names, it's less efficient but contains error handling within the function itself:
Sub SO()
'// Example how to call function
Debug.Print GetCellName(Range("A1"))
End Sub
Function GetCellName(myCell As Excel.Range) As Variant
Dim nameCheck As Variant
For Each nameCheck In Application.Names
If Replace(Replace(Replace(nameCheck, "=", ""), "'", ""), "!", "") = _
CStr(myCell.Parent.Name & myCell.Address) Then
GetCellName = CStr(nameCheck.Name)
Exit Function
End If
Next
GetCellName = CVErr(Excel.xlErrName)
End Function
Note you can also use this function in a worksheet cell like so:
=GetCellName(A1)
Perhaps this would work. This function returns the names assigned to a cell (or bigger range for that matter). If there's more than one name, it returns it as an array for array formula...or the user can supply an index to return only the desired name position
Public Function CellIsInRangeNames(sheetname As String, checkRange As Range, Optional itemNumber As Variant) As Variant
Dim oNM As Name
Dim oSht As Worksheet
Dim isect As Range
Dim namesCollection() As Variant
Set oSht = Worksheets(sheetname)
Dim i As Integer
i = -1
For Each oNM In oSht.Names
Set isect = Application.Intersect(Range(oNM.Name), checkRange)
If Not isect Is Nothing Then
i = i + 1
ReDim Preserve namesCollection(0 To i)
namesCollection(i) = CStr(oNM.Name)
End If
Next oNM
If i = -1 Then
'didn't find any
CellIsInRangeNames = xlErrName
ElseIf Not IsMissing(itemNumber) Then
'user wanted this instance only
If (itemNumber - 1 > UBound(namesCollection)) Or (itemNumber - 1 < LBound(namesCollection)) Then
CellIsInRangeNames = xlErrValue
Else
CellIsInRangeNames = namesCollection(itemNumber - 1)
End If
Else 'here's the list as an array
CellIsInRangeNames = namesCollection
End If
End Function