I have 3 combo boxes which are dependent with each other. List on cmb2 depends on the value on cmb1. cmb3 list depends on cmb2 value.
My problem is, when I remove/delete the values on all combo boxes, when I click cmb2, it still shows the list of the last value chosen on cmb1 & same goes with cmb3.
What's the code to show it empty if the combo box where it's dependent to doesn't have any value?
EDIT: I think based on added information in the comment that you should look to encapsulate the logic in sub procedures and functions. To best do so, there should be some logic between named ranges and the values in the combobox.
In the below example, I created a function to handle conversions from the value in the independent combobox and the named ranges. You can supply your own connector, but I assumed that any space will be changed to an underscore (so, 'Account Information' will be replaced with 'Account_Information').
Next a subprocedure checks to confirm that the named range exists in the workbook that matches the converted value in the independent combobox. If so, the dependent box takes on that named range as its List property. If not, the dependent combobox is cleared.
The benefit to using this type of a system is that it could be repeated for other comboboxes without rewriting code. See below code and let us know if you need added help.
Private Sub UserForm_Initialize()
With Me.ComboBox1
.AddItem "Account Information"
.AddItem "Other Stuff"
End With
End Sub
Private Sub ComboBox1_Change()
With Me
LoadDependentCBO Me.ComboBox1, Me.ComboBox2
End With
End Sub
Private Function ConvertValueToNamedRange(sValue As String, sConnector As String) As String
'This function takes values in the combobox and converts them to named ranges
'The spaces are replaced with whichever connector is supplied to the function
'NOTE: I created a separate function even though this only uses a built-in function
'in case more implementation code is needed
ConvertValueToNamedRange = Replace(sValue, " ", sConnector)
End Function
Private Sub LoadDependentCBO(cboIndependent As ComboBox, cboDependent As ComboBox)
'This code looks to see if there is a match between the value of the independent combobox
'and a named range in the workbook. If not, the dependent combobox is set to be empty
Dim sTemp As String
Dim rNamedRange As Range
sTemp = ""
On Error Resume Next
sTemp = Names.Item(ConvertValueToNamedRange(cboIndependent.Value, "_")).Name
On Error GoTo 0
If sTemp = "" Then
'There is no matching named range so clear the dependent combobox
cboDependent.Clear
Else
cboDependent.List = Range(sTemp).Value
End If
End Sub
OLD POST:
The code ComboBox1.ListIndex = -1 will set the combo box to empty (change the name of the box to suit). The logic to do so depends on more details. Let us know if you need help with implementation.
Related
I have a VBA user form with 6 list boxes. Each list box contains related data in another list box on the same row e.g. list box 1 has an account code and list box 2 has an account name, etc. When a user selects an item within a listbox I want all other listboxes to select a corresponding row. How can I achieve this using a loop?
I know I can explicitly reference the list items as per
excelcise example
but I feel there must be a way to loop through available listboxes instead of listing them by name.
Private Sub CheckControls()
Dim contr As control
For Each contr In Controls
If TypeName(contr) = "ListBox" Then
Select Case contr.Name
Case "ListBox1":
Case "ListBox2":
Case "ListBox3":
'and so on....
End Select
End If
Next contr
End Sub
After a bit of troubleshooting I think I found the answer in the Controls collection. Within each list box I call a procedure which takes existing listbox index, topIndex (useful when using scrolbars) and its own name.
Private Sub lbox_gl_account_Change()
Call arrangeIndexOfLabels(Me.lbox_gl_account.listIndex,
Me.lbox_gl_account.topIndex, Me.ActiveControl.Name)
End Sub
The arrangeIndexOfLabels procedure loops through all controls in the collection and only affects those that are of type ListBox. It updates listIndex and topIndex for all ListBoxes except for the active one.
'Change index location of other listbox items according to the selected one
Private Sub arrangeIndexOfLabels(lngListIndex As Long, lngTopIndex As Long, strActiveControl As String)
Dim ctrlItem As Control
For Each ctrlItem In Me.Controls
If TypeName(ctrlItem) = "ListBox" Then
'Only changing index position of other list boxes than selected
If ctrlItem.Name <> strActiveControl Then
ctrlItem.listIndex = lngListIndex
ctrlItem.topIndex = lngTopIndex
End If
End If
Next ctrlItem
End Sub
I have a problem in my VBA form in Excel and I'm trying to filter the value of a combobox using VLOOKUP from whatever I type in the textbox. How do I achieve this?
My code is:
Private Sub btnTemplateSearch_Click()
Dim filterInfo As Range
Set filterInfo = Worksheets("InfoDump").Range("E2:F46")
txtTemplateFilter.Text = filterInfo.Columns(2).Value
Me.cboTemplateType.List = Application.WorksheetFunction.VLookup(Me.txtTemplateFilter.Text, filterInfo,2,True)
Below is an example of a block of code that can be used to filter the list of ComboBox entries. I decided to Dim an array variable ListForComboBox at the module level such that all procedures in the module can access it. It gets populated at the form's init event by calling LoadListForComboboxArray. You can make changes to this procedure to update the range of input values or any other changes. The combobox's list property is set to this array.
Here's the caveat: you may want to consider tuning the below for performance. I included a textbox, and in the change event, I make a call to set the global array to a filtered version of the original array, based on textbox value. That means that the code will filter the array everytime you type a letter (so if you type "ABC", it will filter three times, creating a new array each time). You may want to assign that function to a different event (textbox exit, maybe) such that the code only fires once you leave the text box, and only once.
Let me know if you have trouble adapting the code:
Dim ListForCombobox() As String
Private Sub TextBox1_Change()
Me.ComboBox1.List = Filter(ListForCombobox, Me.TextBox1.Value)
Debug.Print "CALLED"
End Sub
Private Sub UserForm_Initialize()
LoadListForComboboxArray
Me.ComboBox1.List = ListForCombobox
End Sub
Private Sub LoadListForComboboxArray()
Dim rng As Range
Set rng = Sheets("Sheet1").Range("A1:A11")
ReDim ListForCombobox(1 To rng.Rows.Count)
For i = 1 To rng.Rows.Count
ListForCombobox(i) = rng(i).Value
Next i
Debug.Print ListForCombobox(1)
End Sub
I have looked at some examples for my question but couldn't find an answer that works.
Background:
I have a list of items (let's say apple, orange, banana) in Sheet1 (A2:A77, which is already a defined range with the name "Liste").
I then have on another sheet (Let's say Sheet2) with several cells where a userform (created with vba code) pops up where the user can choose an item and click OK.
However, due to the nature of the userform (and the list), you can have spelling errors etc and it will still be accepted. So I would like to create a check where it matches the input to the given list (to prevent users from putting anything else in). The userform/code is on purpose to keep it searchable (rather than just a simple data validation list).
Issue:
I tried to create this with vba code that checks the input, matches it to the Sheet1 list and if there is no match, shows a msgbox with a statement. This partially worked (for some letters but not others, very strange).
Here is the code I had:
Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
Dim rSearchRng As Range
Dim vFindvar As Variant
If Not Intersect([B7:B26], Target) Is Nothing Then
Set rSearchRng = Sheet4.Range("Liste")
Set vFindvar = rSearchRng.Find(Target.Value)
If Not vFindvar Is Nothing Then
MsgBox "The Audit Project Name you have entered is not valid. Please try again!", vbExclamation, "Error!"
Selection.ClearContents
End If
End If
Application.EnableEvents = True
End Sub
So I was thinking of creating this error message instead with a simple data validation.
Data validation
I have tried the "list" option (and put it equal to the named range) but that did nothing (no error box showed up)
I have tried "Custom" with the following formula 'SUMPRODUCT(--(B12=Liste)>0)=TRUE (I found this on a post which worked for others when I tried it in the cell it gave me the expected "TRUE/FALSE" results) but still nothing shows up
UPDATE
Tigeravatars data validation recommendations work if you don't have a userform (see comments below).
For it to work with a UserForm, I changed the 'MatchEntry' to TRUE and also deleted any unwanted "change events" from my ComboBox code. The final code I use now is below:
Dim a()
Private Sub CommandButton2_Click()
End Sub
Private Sub UserForm_Initialize()
a = [Liste].Value
Me.ComboBox1.List = a
End Sub
Private Sub ComboBox1_Change()
Set d1 = CreateObject("Scripting.Dictionary")
tmp = UCase(Me.ComboBox1) & "*"
For Each c In a
If UCase(c) Like tmp Then d1(c) = ""
Next c
Me.ComboBox1.List = d1.keys
Me.ComboBox1.DropDown
End Sub
Private Sub CommandButton1_Click()
ActiveCell = Me.ComboBox1
Unload Me
End Sub
Private Sub cmdClose_Click()
Unload Me
End Sub
I thought I show it here in case anyone stumbles across my question.
Thank you!
Select your cells where you want to put the data validation
With the cells selected, go to Data Tab -> Validation
Set "Allow" to "List" and set the Source to =Liste as shown here:
Next go to the "Error Alert" tab and set "Style" to "Warning" and enter the desired text in "Title" and "Error Message" as shown here:
Test it out. You should now have a drop-down list of valid choices, and if you attempt to manually enter an invalid choice you'll get the error message you specified.
As a note, if you want the data validation to completely disallow/prevent any entry not in the list, you'll need to set the Error Allert -> Style to be "Stop" instead of "Warning".
EDIT:
Per comments, it can't be a drop-down list. I highly recommend using a drop-down list for this because it will be the most effective way to cut down on time entering data as well as reduce errors from typos. However, if it absolutely cannot be a drop-down list, then you can use a Custom Data Validation and set the formula to =ISNUMBER(MATCH(B7,Liste,0)) (we are using B7 here because it is the first cell in the range of cells that contains this data validation).
Try the following formula:
=NOT(ISERROR(FIND("-"&A1&"-",(TEXTJOIN(,"-",TRUE,Sheet1!A1:A77)))))
That combines all the texts and then see if what's in the cell occurs in the list. I put it between dashes to prevent it from accepting partials.
I want to take a "list of name" data sets from excel and use it for an Combobox Userform. The user uses a dropdown menu from the Combobox to make his/her selection of a name.
My problem is that I don't know how to apply the "list of name" data sets to the combobox. The range for the list of names is in cell C2:AU2 and its in a worksheet called "DoNotPrint-Names". Once the name is chosen by the user I want it to output it to a cell in "DoNotPrint-Setup" worksheet when the button "Next" is clicked.
Screenshot Part of the Names list:
https://imgur.com/sqsUFmF
Screenshot of Userform:
https://imgur.com/UX8ytrY
I tried the code below which asks the Userform to prepopulate cells from "DoNotPrint - Names" worksheet by transposing it first since its a single row. Not sure how to proceed afterward.
Private Sub UserForm_Initialize()
ComboBox1.List = WorksheetFunction.Transpose(DoNotPrint - Names.Range("C2:AU2"))
End Sub
Select your list and give it name,
Example
Then load that list on your ComboBox1
Code Example
Option Explicit
Private Sub UserForm_Activate()
Dim vList As Variant
For Each vList In [MyList]
Me.ComboBox1.AddItem vList
Next
End Sub
Addendum to found solution
Just in Addition to #0m3r 's valid answer, you can use the array Approach to assign the entire named horizontal list to the comboboxe's .List property in a one liner:
Me.ComboBox1.List = Application.Transpose([myList])
Edit
Alternatively you can use the control's .Column property which seems to be rather unknown:
Me.ComboBox1.Column = [myList].Value
I am working on pulling data out of a standardized form in excel. There is a Forms Control CheckBox that I need the state of. Apparently the only way to get this is from the cell link, where the value is placed into a cell. The problem is, whomever put this form together did not set a cell link. Is there any way to do this using VBA at run time. There are many of these forms that I must go through, so I'm trying to avoid doing it manually.
I think you are referring to a forms checkbox control placed on a worksheet, in which case you can get the state of the control without setting a cell link. Like this:
Sub HTH()
Dim iLoop As Integer
'// Get value of check box by its index
MsgBox (GetCheckBoxState(1))
'// Get value of check box by its name
MsgBox (GetCheckBoxState("Check Box 1"))
'// Loop through all checkboxes and get values
For iLoop = 1 To ActiveSheet.CheckBoxes.Count
MsgBox (GetCheckBoxState(iLoop))
Next
End Sub
Function GetCheckBoxState(vCheckBox As Variant) As String
Select Case ActiveSheet.CheckBoxes(vCheckBox).Value
Case xlOn
GetCheckBoxState = "Checked"
Case xlOff
GetCheckBoxState = "UnChecked"
Case xlMixed
GetCheckBoxState = "Mixed"
End Select
End Function
If you are referring to a check box control on a userform then as Tim pointed out it should be a case of something like:
MsgBox (UserForm1.CheckBox1.Value)