VBA: ComboBox only showing one item after Workbook_Open event - vba

I am attempting to have a Workbook_Open event populate a controls ComboBox
so that when the user goes to the Worksheet("Benchmarking"), they have a pre-populated list to choose from that includes all the items in the array datesArr.
The problem i am having is, upon opening the spreadsheet and navigating to the Worksheet("Benchmarking"), i am only seeing one item in the drop down list:
If i select that item then the list actually populates:
Desired result:
I want the full list to be available from the first time the user tries to make a selection not just after the ComboBox1_Change event is fired.
Having reviewed numerous post e.g. Sometimes the ActiveX Combobox only shows one row, why? , Populating Combo Box on WorkBook Open I have tried several different approaches including the following in the Workbook_Open event code:
.ListFillRange = "DropDownDates"
.List = DateArrToStrAr
I have also looped the array adding the items to ComboBox1. Each time i get the same 1 visible item in drop down result.
Is someone able to tell me where i am going wrong please?
My current code is
1) ThisWorkbook
Private Sub Workbook_Open()
With Worksheets("Benchmarking").OLEObjects("ComboBox1").Object
.Clear
.List = DateArrToStrArr '
End With
End Sub
2) Worksheet("Benchmarking"):
Private Sub ComboBox1_Change() 'QH 2/11/17
Dim datesArr() As String
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Lkup")
datesArr = DateArrToStrArr 'function that reads a named range of dates and converts to string to avoid dd/mm becoming mm/dd
If ComboBox1.Value = vbNullString Then ComboBox1.Value = "01/04/2016"
ComboBox1.List = datesArr
'.....other code
End Sub
Notes:
The array datesArr is populated by the function DateArrToStrArr() which reads in a named range of dates "DropDownDates" (workbook scope) and converts them to a string array. This is then assigned to the ComboBox.
DropDownDates is a dynamic named range with formula =OFFSET(Lkup!$F$16,,,Lkup!$M$12,)
Set-up: Excel 2016 64 bit Windows.

Thanks to #CLR for making me think about recalcs. I decided to hack my way around this with the following:
I have added in Worksheet("Benchmarking") a Worksheet_Activate event and removed the Workbook_Open code. This seems to do the trick
Private Sub Worksheet_Activate()
' ComboBox1.Clear
ComboBox1.List = DateArrToStrArr
End Sub

Related

Check if variant is Null or an array

I have a dynamic range that is being used to set a combobox in vba.
The range starts as A3 (which will contain nothing to start) and goes all the way to A3:A9999, depending on how many elements are in the range.
The code then pulls in the data from the range and stores it in a local variant.
My code in VBA is this:
If tempj <> Null Then
cmb_JobNum.List = tempj
End If
When there are 0 elements in the array, tempj = Null, so it does not attempt to set the list.
When there is 1 element in the array, tempj = [Value of cell], so it will set the list to that single element.
When there is 2 or more elements in the array, tempj is now an array, so trying to equate it to a single element throws a 'type mismatch' error. I have no clue how to update the code so that it doesn't get caught out by that error, since every time that equate is run it will crash.
You could try like this:
Dim i As Long
For i = LBound(tempj) To UBound(templ)
cmb_JobNum.AddItem tempj(i)
Next
This code will loop through your array and add every element in it to the combobox. Thus, if array is empy, then no elelements will be added, when there's >0 elements, then all of them will be added.
Here is an example using a dynamic named range to set the fill
Option Explicit
Public Sub test()
With ThisWorkbook.Worksheets("Sheet6") '<== change as appropriate
.ComboBox1.ListFillRange = .Range("dynRange").Address
End With
End Sub
dynRange formula added via name manager (Ctrl + F3)
=OFFSET(Sheet6!$A$3,0,0,COUNTA(Sheet6!$A:$A),1)
Using a worksheet change event to automatically update the combobox:
You could tie this into a Worksheet_Change event on the range A3:A9999 to update automatically the Combobox.
If tying to an event in the code pane of the sheet containing the combobox you could have the following:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A3:A9999")) Is Nothing Then
Application.EnableEvents = False
Me.ComboBox1.ListFillRange = ThisWorkbook.Worksheets("Sheet6").Range("dynRange").Address
Application.EnableEvents = True
End If
End Sub
Example code run:
Code pane for sheet containing Combobox:
Note:
This is assuming an ActiveX combobox but can easily be update for a Form Control ComboBox.
For a form control swop out lines and use:
With Shapes("Drop Down 2").ControlFormat '<== change to appropriate name
.ListFillRange = ThisWorkbook.Worksheets("Sheet6").Range("dynRange").Address
End With
Edit: For UserForm combobox you can populate in the initialize e.g.
Private Sub UserForm_Initialize()
cb1.RowSource = Sheet1.Range("dynRange").Address
End Sub
Figured it out
If VarType(tempj) <> 0 Then
If VarType(tempj) = 8 Then
cmb_JobNum.AddItem tempj
Else
cmb_JobNum.List = tempj
End If
End If

Use VLOOKUP to pass cell reference to a public variable?

I have a userform that opens on cell change in a column.
That userform contains checkboxes, which all trigger a second userform with a text box which looks up a cell on a hidden sheet for its contents. (The checkbox that's ticked determines which cell the textbox looks for). The user then edits the box, clicks a button, and the new text is written back to the same cell.
This is the VBA for when the checkbox is ticked. It works great. Hooray!
Dim vln As Variant
Dim reta As Worksheet
Set reta = ActiveWorkbook.Sheets("RetailerActivity")
Set vln = ActiveCell.Offset(-1, -3)
UserForm2.TextBox1.Text = Application.WorksheetFunction.VLookup(vln, reta.Range("A1:Z100"), 3, False)
UserForm2.TescoSave.Visible = True
UserForm2.Show
End Sub
When the textbox has been edited, I would like to write it back to the same cell it came from. I figure the easiest way to do that is to have a public variable (as range), and to pass the result of the vlookup into that variable so the second userform can have a line which reads
Private Sub ASave_Click()
publicvariable.Value = TextBox1.Value
userform1.hide
End Sub
Nice and easy, rather than doing a VLookup again. Right?
Either way, I can't seem to set the public variable as the lookup.
Outside of any sub I have
Public bums As Range
And in the code above, after the bit where I've set the text box, I've tried to add the line
Set bums = Application.WorksheetFunction.VLookup(vln, reta.Range("A1:Z100"), 3, False)
But the code errors with a "type mismatch".
If I try
Set bums = Range(Application.WorksheetFunction.VLookup(vln, reta.Range("A1:Z100"), 3, False))
I get method "Range" of object "_global" failed.
I code by cobbling bits off the internet, as you can probably tell, so this is I don't doubt a complete kludge.
Any advice would be super appreciated.
VLookup returns a value, not a Range. You could use Match to find the row and then Cells to get the actual reference - for example:
Dim vMatch
vMatch = Application.Match(vln, reta.Range("A1:A100"),0)
If Not IsError(vMatch) then
Set bums = reta.Cells(vMatch, "C")
else
msgbox "No match for " & vln
Exit Sub
End If
Personally I would also not use a public variable, but create a property for Userform2 to which you can assign the range.

Removing items from multiple comboboxes

I have 7 comboboxes. All these comboboxes have same source.
With Sheets("Data_Sheet")
Sheets("UI_Interface").ComboBox2.ListFillRange = "Data_Sheet!E2:E" & .Cells(Rows.Count, 5).End(xlUp).Row
End With
Same code has been written for other combobxes.
Now when a value from combobx1 is selected it should not be present in other comoboxes.
When i try to do this with following code but i'm getting error with this code.
j = ComboBox1.ListIndex
ComboBox2.RemoveItem (j)
I tried some different attributes too for removing the value but all gave some exception.
What is incorrect in this code?
The RemoveItem method works properly for me unless I use the .ListFillRange method to populate the combobox. If you use the .List method instead, it should work. To do that, you have to convert the range to an array.
REVISED
Thanks to enderland for pointing out that you are working with form controls on a worksheet, not in a user form. So the approach should be similar but you won't be able to use the ListFillRange method. Not a big deal, we can easily take that range, convert it to a variant/array, and use the List method.
Option Explicit
Private Sub Worksheet_Activate()
'## Sets default list for ComboBoxes on this sheet
SetComboBoxLists ComboBox1
SetComboBoxLists ComboBox2
End Sub
Private Sub ComboBox1_Change()
'## event handler for a combobox, repeat as needed
'## Repopulate the list, otherwise you may get
' an Index out of Range error or Invalid Argument error,
' or the RemoveItem method will remove the wrong item
SetComboBoxList ComboBox2
'## Now, remove the item selected in ComboBox1
ComboBox2.RemoveItem ComboBox1.ListIndex
End Sub
Private Sub SetComboBoxLists(cBox As MSForms.ComboBox)
'## Subroutine to fill the list in each combobox as needed
Dim lstRange As Variant
'## Populate the array variable to use for the combobox.List method:
' double-check that I put the parentheses in the right place!
With Sheets("Data_Sheet")
lstRange = .Range("E2:E" & .Cells(Rows.Count, 5).End(xlUp).Row)
End With
'## Populate the combobox with the list
cBox.List = lstRange
End Sub
Note that if any of your code manipulates (e.g., resizes, removes rows, etc) the range, you'll need to re-apply the List method.

Is there a way to use an input value (textbox) of a userform as a variable for another userform?

Ok so what I am trying to accomplish: I have one userform that asks how many new orders the user needs to process. I set the user input to a variable in this code (not sure if it even does anything).
Private Sub CommandButton1_Click()
UserForm2.Show
OrderNum.Text = NewOrders
'I changed the textbox name to OrderNum
End Sub
Then when UserForm2 pops up, I want to be able to input more data with more specific information about the orders. So if on Userform1 I entered in 3, I want to have to submit new data into UserForm2 3 different times. I tried using a For - Next loop (below) but it doesn't work. I'm not sure how (or if) I can store variables like that between Userforms.
Private Sub CommandButton1_Click()
Dim lRow As Long
Dim ws As Worksheet
Set ws = Worksheets("Core Info")
OrderNum.Text = NewOrders
lRow = ws.Cells(Rows.Count, 2).End(xlUp).Offset(1, 0).Row
For i = 1 To NewOrders
ws.Cells(lRow, 1).Value = TextBox1.Text
ws.Cells(lRow, 3).Value = TextBox2.Text
Next i
UserForm2.Hide
End Sub
The the second userform pops up as it should, but then nothing works after that. Can anyone tell me what I could do to fix this?
Note: I realize that those of the above start with CommandButton1 (default) but they are on different Userforms.
It will be helpful (and is good coding practice) to give your form controls more easily recognizable names, instead of the ambiguous CommandButton1 nomenclature. Revise CommandButton1's name on UserForm1 to Form1_SubmitButton. Then, use this code to handle the form submission.
Sub Form1_SubmitButton_Click()
' a textbox control named OrderNum on UserForm1 has captured the value
'Assign the value from the OrderNum textbox to a named variable in the worksheet
ActiveWorkbook.Names.Add "OrderNum", Me.OrderNum.Text
UserForm2.Show
End Sub
Then, in UserForm2 change the name of your command button to something like Form2_SubmitButton and use this code:
Sub Form2_SubmitButton_Click()
Dim orderNum as Long
orderNum = Replace(ActiveWorkbook.Names("OrderNum").Value,"=",vbNullString)
'the rest of your code goes here.
End Sub
So, what we have done above is to create a Name in the worksheet which contains the value you want to use on the several forms. You can always obtain this value by Replace(ActiveWorkbook.Names("OrderNum").Value,"=",vbNullString)
Alternatively, you could use a public variable in your code module.

Detecting changes to checkboxes via VBA

Following on from my previous question.
A requirement from the customer is to have checkboxes on a report to disable rows of information on another sheet. The rows are defined as named ranges, formated by P_XXXXXX. The XXXXXX is a unique identifier that is also a field on the row so I can easily generate the range names on the fly.
The problem I am having is:
After clicking on the items and then closing the form Excel asks if we want to save. This is undersirable.
I need someway of registering a change event happening on my generated checkboxes. So if one or more changes I can run through and hide/unhide the relevant ranges.
My code for adding the checkboxes looks like:
' For each row...
' check box in column 17(=Q).
Dim lCenter As Long
lCenter = rngCurrent.Width / 4 ' not actual centre but close enough
With ActiveSheet.CheckBoxes.Add(rngCurrent.Left + lCenter, rngCurrent.Top - 2, rngCurrent.Width, rngCurrent.Height)
.Interior.ColorIndex = xlNone
.Caption = ""
End With
So how do you link a change in a checkbox with a sub/function?
Set the OnAction property of the Checkboxes object to the name of a sub you want to run whenever the checkbox is checked or unchecked.
Sub MakeCB()
With ActiveSheet.CheckBoxes.Add(ActiveCell.Left + 0, ActiveCell.Top - 2, ActiveCell.Width, ActiveCell.Height)
.Interior.ColorIndex = xlNone
.Caption = ""
.OnAction = "CheckboxChange"
End With
End Sub
Sub CheckboxChange()
MsgBox "change"
End Sub
I don't think there are any events available with the Excel.Checkbox control. Try using the MSForms checkbox instead. You'll need a reference to 'Microsoft Forms 2.0 Object Library' - it's not redistributeable, but if you're using VBA, then that's fine.
You can then do something like this, and handle the event in the usual way:
''class level
Private WithEvents m_Checkbox as MSForms.CheckBox
Public Sub MakeCheckbox()
Set m_Checkbox = Activesheet.OLEObjects.Add("Forms.Checkbox.1")
End Sub
Private Sub m_Checkbox_Click()
''Do stuff
End Sub
Obviously, you'll only be able to handle a set number of checkboxes this way - I would recommend creating a class to hold each checkbox.