Refresh combo box after list update vba excel - vba

I'm trying to auto update a combobox list. It updates correctly only when I close and then open the workbook, or when I press the stop button on VBA and run the macro again. I have the following VBA code.
Private Sub UserForm_Initialize()
Dim cod As Range
Dim pro As Range
Dim cli As Range
Dim ws As Worksheet
Dim ws5 As Worksheet
Set ws = Worksheets("ListaProductos")
Set ws5 = Worksheets("ListaClientes")
For Each cod In ws.Range("CodigoProductoLista")
With Me.codigo
.AddItem cod.Value
.List(.ListCount - 1, 1) = cod.Offset(0, 1).Value
End With
Next cod
For Each cli In ws5.Range("ClienteLista")
With Me.cliente
.AddItem cli.Value
.List(.ListCount - 1, 1) = cli.Offset(0, 1).Value
End With
Next cli
No.Value = True
calendario2.Visible = False
calendario2.Refresh
calendario = Date
Me.codigo.SetFocus
End Sub
Thanks!

You could call the UserForm_Initialize procedure again, but you will have to clear the lists first. You could use it in a commandbutton, or in an event for instance.

That Initialize event will only trigger when the form is loading. Add a button to your form called cmdRepopulate and use this code instead:
Option Explicit
Private Sub UserForm_Initialize()
PopulateCodigoProductoLista
PopulateClienteLista
FinishingOff
End Sub
Private Sub PopulateCodigoProductoLista()
Dim rngData As Range
With Worksheets("ListaProductos").Range("CodigoProductoLista")
Set rngData = .Resize(.Rows.Count, 2)
End With
PopulateComboUsingRange Me.codigo, rngData
End Sub
Private Sub PopulateClienteLista()
Dim rngData As Range
With Worksheets("ListaClientes").Range("ClienteLista")
Set rngData = .Resize(.Rows.Count, 2)
End With
PopulateComboUsingRange Me.cliente, rngData
End Sub
Private Sub FinishingOff()
No.Value = True
calendario2.Visible = False
calendario2.Refresh
calendario = Date
Me.codigo.SetFocus
End Sub
Private Sub PopulateComboUsingRange(cboDataDestination As MSForms.ComboBox, _
rngDataSource As Range)
Dim lngCounter As Long
With cboDataDestination
.Clear
For lngCounter = 1 To rngDataSource.Rows.Count
.AddItem rngDataSource.Cells(lngCounter, 1)
If rngDataSource.Columns.Count = 2 Then
.List(.ListCount - 1, 1) = rngDataSource.Cells(lngCounter, 2)
End If
Next
End With
End Sub
Private Sub cmdRepopulate_Click()
PopulateCodigoProductoLista
PopulateClienteLista
FinishingOff
End Sub
There are some limitations (because it's too late), notably the generic combo population routine but in its current for it should be good for you. The code will run on the form's Initialize event and whenever you click the repopulate button. I decided to work with your named ranges as they were - no changes.
Note the use of a generic proc to populate the combo. If you had other combos on other forms in this same workbook, you could move that proc to a separate module, change it to Public (rather than Private) and save yourself a lot of typing by reusing the code.

Related

ComboBox trying to set selected item results in Compile error

I'm trying to select an item in a ComboBox in a UserForm. I found the .Selected(index)=True code almost everywhere but for me it sais:
Compile error: Method or data member not found.
My code:
Private Sub UserForm_Initialize()
Dim worksheetList As New ArrayList
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
worksheetList.Add ws.Name
Next ws
sourceWorksheets.List = worksheetList.toArray
destinationWorksheets.List = worksheetList.toArray
sourceWorksheets.Selected(1) = True 'Error here
End Sub
Am I doing something wrong? I couldn't really find any other function which would set the "default" item.
As #Rory keeps saying - use ListIndex to select an item in the list control.
This piece of code will add each sheet name to the list control and then select the first item:
Private Sub UserForm_Initialize()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
Me.worksheetList.AddItem ws.Name
Next ws
Me.worksheetList.ListIndex = 0
End Sub
I think the OP was trying to use the code similar to below, but this still needs the ListIndex=0.
Private Sub UserForm_Initialize()
Dim ws As Worksheet
With CreateObject("System.Collections.ArrayList")
For Each ws In ThisWorkbook.Worksheets
.Add ws.Name
Next ws
Me.worksheetList.List = .ToArray
End With
Me.worksheetList.ListIndex = 0
End Sub
Edit: The code assumes the list control is called worksheetList.
Edit 2: A slightly different version. It reverses the items in the list when you click the form.
It's still Me.worksheetList.ListIndex = 0 to select the item in the list control though.
Option Explicit
Public MyArrayList As Variant
Private Sub UserForm_Initialize()
Dim ws As Worksheet
Set MyArrayList = CreateObject("System.Collections.ArrayList")
With MyArrayList
For Each ws In ThisWorkbook.Worksheets
.Add ws.Name
Next ws
.Sort
Me.worksheetList.List = .ToArray
End With
Me.worksheetList.ListIndex = 0
''This will only work in a listbox, not a combobox.
''Select items in row numbers that are even (ListIndex 0,2,4, etc)
''MultiSelect must be 1 - fmMultiSelectMulti or 2 - fmMultiSelectExtended
' Dim x As Long
' For x = 0 To Me.worksheetlist.ListCount - 1
' If x Mod 2 = 0 Then
' Me.worksheetlist.Selected(x) = True
' End If
' Next x
End Sub
Private Sub UserForm_Click()
With MyArrayList
.Reverse
Me.worksheetList.List = .ToArray
End With
Me.worksheetList.ListIndex = 0
End Sub
To check whether particular element (indicated by index) is selected you should do workaround like this:
ComboBox1.Value = ComboBox1.List(i)
where i is given index. It has to be done like that, because there is no propertry like SelectedIndex in VBA ComboBox.
Keep in mind, that indexing starts with 0 !

Getting a value from ComboBox

I have created a combo-box via VBA code.
Sub CreateFormControl()
ActiveSheet.DropDowns.Add(0, 0, 100, 15).Name = "ComboBox1"
ActiveSheet.Shapes("ComboBox1").ControlFormat.RemoveAllItems
Dim i As Integer
With ActiveSheet.Shapes("ComboBox1").ControlFormat
For i = 1 To 25
.AddItem i
Next i
End With
ActiveSheet.Shapes.Range(Array("ComboBox1")).Select
Selection.OnAction = "ComboBox1_Change"
Range("B2").Select
End Sub
The problem here is, when I select an item in the ComboBox, it gives me a
Run-time error 424. Object required
It doesn't show the value selected. I also tried to change my declaration, Sub CreateFormControl() to Public Sub CreateFormControl(), but it's still not working.
Sub ComboBox1_Change()
MsgBox (ComboBox1.Value) 'The error is here
End Sub
Try the code below, try to replace ActiveSheet with a qualifed Worksheet, like Worksheets("YoutSheetName").
Sub ComboBox1_Change()
Dim ws As Worksheet
Dim MyDropDown As DropDown
' try not to use ActiveSheet, replace "Sheet1" with your sheet's name
Set ws = Worksheets("Sheet1") ' ActiveSheet
Set MyDropDown = ws.Shapes("ComboBox1").OLEFormat.Object ' <-- set my Object with "ComboBo1" drop-down
MsgBox MyDropDown.List(MyDropDown.ListIndex) '<-- display the value of the selected item
End Sub
Below is a "cleaner" way to add a new DropDown to a Worksheet without using ActiveSheet, Select and Selection (just use fully qualified objects).
Sub CreateFormControl Code
Option Explicit
Sub CreateFormControl()
Dim MyDropDown As DropDown
Dim i As Long
' set the drop-down object to the new created drop-down (replace "Sheet1" with your sheet's name)
Set MyDropDown = Worksheets("Sheet1").DropDowns.Add(0, 0, 100, 15)
' modify the drop-down properties
With MyDropDown
.Name = "ComboBox1"
.RemoveAllItems
For i = 1 To 25
.AddItem i
Next i
.OnAction = "ComboBox1_Change"
End With
End Sub

Error passing array to listbox as a parameter

Objective : Create a userform and take a user input, and then from user input put it in a list and when you click the list it automatically find it in the whole workbook.
Something Like this:
I saw this post: Match in whole workbook
And I created something out of that:
Public Sub CommandButton3_Click()
Dim TempArray As Variant
Dim RealArray()
TempArray = Application.InputBox("Select a range", "Obtain Range Object", Type:=64)
ArrayRows = UBound(TempArray)
ReDim RealArray(1 To ArrayRows)
For i = 1 To ArrayRows
RealArray(i) = TempArray(i, 1)
Next i
MsgBox "The number if rows selected are " & ArrayRows
ListBox1.List = RealArray
ListBox1 Arraay:=RealArray
End Sub
Public Sub ListBox1_Click(ByRef Arraay() As Variant)
Dim Sh As Worksheet
Dim something As Range
Dim ArrayRows As Long
For Each Sh In ThisWorkbook.Worksheets
With Sh.UsedRange
For i = 1 To ArrayRows
Set something = RealArray.Find(What:=RealArray(i))
If Not something Is Nothing Then
Do Until something Is Nothing
test = something.Value
Set something = .FindNext(something)
Loop
End If
Next i
End With
Set something = Nothing
Next
End Sub
After creating this, I get an error regarding the second sub.
procedure declaration does not match description of event or procedure having the same name
The Listbox click event doesn't take any parameters.
Private Sub ListBox1_Click()
End Sub
If you want to pass an array between sub then you can so it this way
Dim MyArray() As Variant
Public Sub CommandButton3_Click()
'~~> Initialize array
End Sub
Private Sub ListBox1_Click()
'~~> Use array here
'~~> Also put an error check if the array is initialized or not
End Sub

Macro on multiple worksheets: Need summary worksheet to control macros on other sheets

I have a timer macro on multiple, identical worksheets. My users will time their task time and each worksheet represents a different task. I need to have a summary sheet with the macros that start and stop time that is linked to each worksheet so that my users don't have to toggle back and forth between sheets to start the timers for each task. Can you help. Here is the timer code I'm using. It works well on each worksheet, but I don't know how to code the buttons on a summary worksheet to activate this code on a specific worksheet. Here's the code:
Sub startStopTimer()
If Range("j4") = "Start" Then
Range("$b$8").Offset(Range("j6") + 1).Value = Now
Range("j4") = "Stop"
Else
Range("$b$8").Offset(Range("j6"), 1).Value = Now - Range("$b$8").Offset(Range("j6"))
Range("$j$4") = "Start"
End If
End Sub
I'm not sure you need to call your timer routine in each worksheet. You really only need one routine and knowledge of which worksheet to assign the times to.
One way would be with a kind of control panel of buttons on a UserForm. It might look something like this (just 3 worksheets as example):
Then you'd handle all of the click events within the UserForm code. In this example, I've created a collection of Worksheets and each item is accessed by a string key which is the button's name. Skeleton code would be:
Option Explicit
Private Const START_COLOUR As Long = &HFF00&
Private Const START_TEXT As String = "Start"
Private Const STOP_COLOUR As Long = &HFF&
Private Const STOP_TEXT As String = "Stop"
Private mSheets As Collection
Private Sub btnClock1_Click()
StartStopButton btnClock1
End Sub
Private Sub btnClock2_Click()
StartStopButton btnClock2
End Sub
Private Sub btnClock3_Click()
StartStopButton btnClock3
End Sub
Private Sub StartStopButton(btn As CommandButton, Optional initialise As Variant)
Dim ws As Worksheet
Dim v As Variant
Dim startTime As Date
Set ws = mSheets(btn.Name)
ws.Activate
If Not IsMissing(initialise) Then
'Initialise the button and sheet
SetProperties btn, CBool(initialise)
ws.Range("A1").Value = "Not yet actioned"
ws.Range("B1:D1").ClearContents
Else
If btn.BackColor = START_COLOUR Then
'Set clock running
SetProperties btn, True
ws.Range("A1").Value = "Running"
ws.Range("B1").Value = Now
ws.Range("C1:D1").ClearContents
Else
'Stop clock and calculate difference
SetProperties btn, False
ws.Range("A1").Value = "Stopped"
ws.Range("C1").Value = Now
v = ws.Range("B1").Value
If Not IsEmpty(v) And IsDate(v) Then
'For DateDiff, choose whichever unit you want, I've used seconds ("s")
ws.Range("D1").Value = DateDiff("s", v, Now)
End If
End If
End If
End Sub
Private Sub SetProperties(btn As CommandButton, running As Boolean)
With btn
If running Then
.Caption = STOP_TEXT
.BackColor = STOP_COLOUR
Else
.Caption = START_TEXT
.BackColor = START_COLOUR
End If
End With
End Sub
Private Sub UserForm_Initialize()
Dim ws As Worksheet
'Assign all worksheets to collection
Set mSheets = New Collection
Set ws = ThisWorkbook.Worksheets("Sheet1")
mSheets.Add ws, btnClock1.Name
Set ws = ThisWorkbook.Worksheets("Sheet2")
mSheets.Add ws, btnClock2.Name
Set ws = ThisWorkbook.Worksheets("Sheet3")
mSheets.Add ws, btnClock3.Name
'Set all buttons to start
StartStopButton btnClock1, False
StartStopButton btnClock2, False
StartStopButton btnClock3, False
End Sub

Clear cascading ComboBox on reselection

I am in the process of creating a user form in excel that uses several ComboBox'. The first ComboBox lists values from column 1 of a table and the following ComboBox' lists values from the following columns. ComboBox 2 onwards also only lists values depending on the preceding box. All ComboBox' show unique values only.
Here is the current code I am using:
Option Explicit
Private Sub ComboBox1_Change()
Call cValues(ComboBox1.Value, ComboBox2, 2)
End Sub
Private Sub ComboBox2_Change()
Call cValues(ComboBox2.Value, ComboBox3, 3)
End Sub
Private Sub ComboBox3_Change()
Call cValues(ComboBox3.Value, ComboBox4, 4)
End Sub
Private Sub ComboBox4_Change()
Call cValues(ComboBox4.Value, ComboBox5, 5)
End Sub
Private Sub ComboBox5_Change()
Call cValues(ComboBox5.Value, ComboBox6, 6)
End Sub
Private Sub UserForm_Initialize()
Dim Rng As Range
Dim Dn As Range
Dim Dic As Object
With Sheets("Listuni")
Set Rng = .Range(.Range("A2"), .Range("A" & Rows.Count).End(xlUp))
End With
Set Dic = CreateObject("scripting.dictionary")
Dic.CompareMode = vbTextCompare
For Each Dn In Rng: Dic(Dn.Value) = Empty: Next
Me.ComboBox1.List = Application.Transpose(Dic.keys)
End Sub
Sub cValues(txt As String, Obj As Object, col As Integer)
Dim Dn As Range
Dim Rng As Range
Dim Dic As Object
With Sheets("Listuni")
Set Rng = .Range(.Cells(2, col), .Cells(Rows.Count, col).End(xlUp))
End With
Set Dic = CreateObject("Scripting.Dictionary")
Dic.CompareMode = 1
For Each Dn In Rng
If Dn.Offset(, -1).Value = txt Then
If Not Dic.exists(Dn.Value) Then
Dic(Dn.Value) = Empty
End If
End If
Next Dn
Obj.List = Application.Transpose(Dic.keys)
End Sub
The problem I am having occurs when a user makes a reselection of a preceding ComboBox. Instead of clearing the subsequent boxes, all existing selections remain.
I am looking for a way to clear/default the values of subsequent ComboBox every time a reselection of a preceding ComboBox is made. For example if I make a selection in ComboBox 1 and 2 but then change my selection at ComboBox 1, I want ComboBox 2 to clear rather than show my previous selection. Note that the default position for the user form on launch shows no values in any ComboBox.
I have tried using the .clear method on change however this always gets hung up at:
Obj.List = Application.Transpose(Dic.keys)
I suspect this is because a clear is technically a change and therefore it cannot transpose the list of values to other boxes based on a null value.
This clears all subsequent ComboBoxes - if Combo1 changes, Combo2, 3, 4, 5, and 6 are cleared
Option Explicit
Private ws As Worksheet
Private d As Object
Private Sub UserForm_Initialize()
Dim cel As Range, txt As String, rng As Range
Set ws = Worksheets("Listuni")
Set d = CreateObject("Scripting.Dictionary"): d.CompareMode = vbTextCompare
Set rng = ws.Range(ws.Cells(2, 1), ws.Cells(ws.Rows.Count, 1).End(xlUp))
For Each cel In rng: d(cel.Value) = Empty: Next
ComboBox1.List = Application.Transpose(d.keys)
End Sub
Private Function setList(ByVal txt As String, ByRef cmb As ComboBox) As Object
Dim xID As Long, rng As Range, cel As Range, x As Control
xID = Right(cmb.Name, 1)
For Each x In Me.Controls
If TypeName(x) = "ComboBox" Then If Val(Right(x.Name, 1)) > xID - 1 Then x.Clear
Next
Set rng = ws.Range(ws.Cells(2, xID), ws.Cells(ws.Rows.Count, xID).End(xlUp))
d.RemoveAll
For Each cel In rng
If cel.Offset(, -1) = txt Then
If Not d.exists(cel.Value) Then
d(cel.Value) = Empty
End If
End If
Next
If d.Count > 0 Then cmb.List = Application.Transpose(d.keys) Else cmb.Clear
End Function
Private Sub ComboBox1_Change()
setList ComboBox1.Value, ComboBox2
End Sub
Private Sub ComboBox2_Change()
setList ComboBox2.Value, ComboBox3
End Sub
Private Sub ComboBox3_Change()
setList ComboBox3.Value, ComboBox4
End Sub
Private Sub ComboBox4_Change()
setList ComboBox4.Value, ComboBox5
End Sub
Private Sub ComboBox5_Change()
setList ComboBox5.Value, ComboBox6
End Sub