Populate ComboBox With Sheet Names Dynamically - vba

I am having trouble with populating a combo box on a excel ribbon dynamically.
I wish for the combo box to be populated using the names of the sheets of the workbook dynamically.
I am able to select the sheet names already presentin the combo box that is placed on the ribbon however I do not seam to be able to code the VBA to populate the combo box with the sheet names if I add them or modify the name.
I have written below code but its not working :
Sub SelectionFeuille_GetItemLabel(control As IRibbonControl, index As Integer, ByRef returnedVal)
Dim dTime As Date
dTime = Now + TimeValue("00:00:01") 'hh:mm:ss
Application.OnTime dTime, "Refresh_all"
returnedVal = ActiveWorkbook.Worksheets(index + 1).Name
End Sub
Please help me....

The simplest way I've found to do this is to capture the Calculate event, and I do that by having a hidden worksheet with formulae to each sheet in its cells. It's far from perfect and, if truth be told, is a pretty ugly workaround, but at least it's food for thought for you. I guess a timer would also work but that seems just as ugly.
All of this code goes in the code behind your workbook:
Option Explicit
Private Const NAMES_SHEET As String = "Hidden|Sheet|Names"
Private mNamesSheet As Worksheet
Private Sub Workbook_Open()
Dim b As Boolean
b = Application.ScreenUpdating
On Error Resume Next
Set mNamesSheet = ThisWorkbook.Worksheets(NAMES_SHEET)
On Error GoTo 0
If mNamesSheet Is Nothing Then
Application.ScreenUpdating = False
Set mNamesSheet = ThisWorkbook.Worksheets.Add
mNamesSheet.Name = NAMES_SHEET
mNamesSheet.Visible = xlSheetVeryHidden
End If
WriteNamesOfSheets
Application.ScreenUpdating = b
End Sub
Private Sub WriteNamesOfSheets()
Dim v() As Variant
Dim ws As Worksheet
Dim i As Integer
Dim b As Boolean
b = Application.EnableEvents
Application.EnableEvents = False
ReDim v(1 To ThisWorkbook.Worksheets.Count, 1 To 1)
mNamesSheet.Cells.Clear
i = 0
For Each ws In ThisWorkbook.Worksheets
If ws.Visible = xlSheetVisible Then
i = i + 1
v(i, 1) = "=" & ws.Name & "!A1"
End If
Next
mNamesSheet.Range("A1").Resize(UBound(v, 1)).Formula = v
Application.EnableEvents = b
End Sub
Private Sub Workbook_SheetCalculate(ByVal Sh As Object)
Dim ws As Worksheet
Dim b As Boolean
On Error GoTo EH
b = Application.EnableEvents
Application.EnableEvents = False
WriteNamesOfSheets
For Each ws In ThisWorkbook.Worksheets
If ws.Visible = xlSheetVisible Then
'
'Populate your combobox here with ws.Name
'
End If
Next
Application.EnableEvents = b
Exit Sub
EH:
Err.Clear
End Sub

Related

Unable to Activate Macro when Active Cell Changes Through formula

My aim is to trigger the advanced filter macro when cell B2 changes (a part of the filtering criteria). B2 is linked to another cell(in another worksheet) which dynamically gets data from external sources. The problem I am facing is that the macro does not activate automatically. Only when I manually change something in B2 is the macro activated. Otherwise the old criteria remains in place. A1 to G1 has the 7 categories and A2-G2 has the inputs for the filter. Only B2 changes effectively. I have not coded in VBA before so most of this code is copied from websites and modified for my workbook. Below is my code. Appreciate any help on this.
Option Explicit
'Create variable to hold values
Dim Monitored()
Sub Advanced_Filtering()
Range("A7:G730").AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=Range("A1:G2"), CopyToRange:=Sheets("Sheet3").Range("L1:R1")
End Sub
Private Sub Worksheet_Activate()
Monitored = Range("B2:C2").Value 'Read in value prior to any changes
End Sub
Private Sub Worksheet_Calculate()
Dim Xrg As Range, c As Range, x As Integer
Set Xrg = Range("B2:C2")
If Not Intersect(Xrg, Range("B2:C2")) Is Nothing Then
Application.EnableEvents = False
'Compare monitored cell with initial value
x = 1
For Each c In Range("B2:C2")
If c.Value <> Monitored(x, 1) Then
Call Advanced_Filtering
Monitored(x, 1) = c.Value
End If
x = x + 1
Next c
'Reset events
Application.EnableEvents = True
End If
End Sub
Probably the easiest fix would be to place the Worksheet_Change event under the cells that generate the value on your cell B2, as changes in formula values don't trigger the Change event... or you can change it to Worksheet_Calculate event instead, this will pick up changes in formula results as below:
Option Explicit
'Create variable to hold values
Dim Monitored
Sub Advanced_Filtering()
Range("A7:G730").AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=Range("A1:G2"), CopyToRange:=Sheets("Sheet3").Range("L1:R1")
End Sub
Private Sub Worksheet_Activate()
Monitored = Range("B2").Value 'Read in value prior to any changes
End Sub
Private Sub Worksheet_Calculate()
Dim Xrg As Range
Set Xrg = Range("B2")
If Not Intersect(Xrg, Range("B2")) Is Nothing Then
Application.EnableEvents = False
'Compare monitored cell with initial value
If Range("B2").Value <> Monitored Then
'Do things as a result of a change
Call Advanced_Filtering
'Reset Variable with new monitored value
Monitored = Range("B2").Value
End If
'Reset events
Application.EnableEvents = True
End If
End Sub
UPDATE:
To use a Range of cells instead of a single one, you should change the following:
Option Explicit
'Create variable to hold values
Dim Monitored()
Sub Advanced_Filtering()
Range("A7:G730").AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=Range("A1:G2"), CopyToRange:=Sheets("Sheet3").Range("L1:R1")
End Sub
Private Sub Worksheet_Activate()
Monitored = Range("B2:C2").Value 'Read in value prior to any changes
End Sub
Private Sub Worksheet_Calculate()
Dim Xrg As Range, c As Range, x As Integer
Set Xrg = Range("B2:C2")
If Not Intersect(Xrg, Range("B2:C2")) Is Nothing Then
Application.EnableEvents = False
'Compare monitored cell with initial value
x = 1
For Each c In Range("B2:C2")
If c.Value <> Monitored(1, x) Then
Call Advanced_Filtering
Monitored(1, x) = c.Value
End If
x = x + 1
Next c
'Reset events
Application.EnableEvents = True
End If
End Sub

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 !

User Form Run-time error '424': Object required Excel VBA

I have a user form and on a click of a button it is supposed to access or open a user form. But every time code gets to that part,
Run-time error '424':
Object required
pops up. Here is my code:
If CheckSheet(TextBoxValue) = True Then
Sheets(TextBoxValue).Select
UserForm.Show
Else
Set Worksheet = ThisWorkbook.Sheets.Add(After:=Sheets(Sheets.Count))
Worksheet.Name = TextBoxValue
Dim label As Control
For Each label In UserForm.Controls
If TypeName(label) = "Label" Then
With ActiveSheet
i = i + 1
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
.Cells(lastRow, i).Value = label.Caption
End With
End If
Next
UserForm.Show
End If
Every time it gets to the part with UserForm.Show and For Each label In UserForm.Controls
I checked the spelling of the form multiple times already and it is very much the same.
You may have had something like this in mind:-
Sub TestCode()
Dim Ws As Worksheet ' "Worksheet" is a reserved word
Dim MyForm As UserForm1 ' "UserForm" is a reserved word
Dim MyLabel As Control ' "Label" is a reserved word
Dim C As Long ' better name for a column than "i"
Set MyForm = New UserForm1
If GetSheet(Ws) Then
For Each MyLabel In MyForm.Controls
If TypeName(MyLabel) = "Label" Then
With Ws ' true, Ws is the ActiveSheet but
' always use the same name for the same sheet
C = C + 1
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
.Cells(LastRow, C).Value = MyLabel.Caption
End With
End If
Next
End If
MyForm.Show
End Sub
Private Function GetSheet(Ws As Worksheet) As Boolean
' return True if Ws didn't exist
Dim Ws As Worksheet
On Error Resume Next
Set Ws = Worksheets(TextBoxValue)
If Err Then ' Err = doesn't exist
Set Ws = ThisWorkbook.Sheets.Add(After:=Sheets(Sheets.Count))
Ws.Name = TextBoxValue
GetSheet = True
End If
End Function
Private Function TextBoxValue() As String
TextBoxValue = "MySheetName"
End Function
In order to test if a word is a "reserved" word, select it in your VB Editor and press F1. If MS Office uses it, don't argue.

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

Using textboxes within userform to define variables?

I currently run a macro to compare the most recent sheet of data to the report immediately prior and highlight changes. It works fine on its own. Now, however, we would like to be able to compare selected sheets from any time period. My idea was to pop up a simple userform with two textboxes that the user can use to specify which two reports he wants to compare. I am quite lost though with the idea of trying to declare public variables; what I've got atm is:
Option Explicit
Public shtNew As String, shtOld As String, _
TextBox1 As TextBox, TextBox2 As TextBox
Sub SComparison()
Const ID_COL As Integer = 31 'ID is in this column
Const NUM_COLS As Integer = 31 'how many columns are being compared?
Dim rwNew As Range, rwOld As Range, f As Range
Dim X As Integer, Id
shtNew = CSManager.TextBox1
shtOld = CSManager.TextBox2
'Row location of the first employee on "CurrentMaster" sheet
Set rwNew = shtNew.Rows(5)
Do While rwNew.Cells(ID_COL).Value <> ""
Id = rwNew.Cells(ID_COL).Value
Set f = shtOld.UsedRange.Columns(ID_COL).Find(Id, , xlValues, xlWhole)
If Not f Is Nothing Then
Set rwOld = f.EntireRow
For X = 1 To NUM_COLS
If rwNew.Cells(X).Value <> rwOld.Cells(X).Value Then
rwNew.Cells(X).Interior.Color = vbYellow
rwNew.Cells(33) = "UPDATE"
Else
rwNew.Cells(X).Interior.ColorIndex = xlNone
End If
Next X
End If
Set rwNew = rwNew.Offset(1, 0) 'next row to compare
Loop
Call SUpdates
End Sub
My Suggestion would be to use Comboboxes instead of TextBoxes. Create a userform with two command buttons and two comboboxes and populate the comboboxes in the UserForm_Initialize() event using this code.
Private Sub UserForm_Initialize()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Sheets
ComboBox1.AddItem ws.Name: ComboBox2.AddItem ws.Name
Next
End Sub
And then use this code in the OK button to do the comparison.
Private Sub CommandButton1_Click()
Dim shtNew As Worksheet, shtOld As Worksheet
If ComboBox1.ListIndex = -1 Then
MsgBox "Please select the first sheet"
Exit Sub
End If
If ComboBox2.ListIndex = -1 Then
MsgBox "Please select the Second sheet"
Exit Sub
End If
Set shtNew = Sheets(ComboBox1.Value)
Set shtOld = Sheets(ComboBox2.Value)
'~~> REST OF THE CODE HERE NOW TO WORK WITH THE ABOVE SHEETS
End Sub
Private Sub CommandButton2_Click()
Unload Me
End Sub
HTH
Sid
For an easy fix, couldn't you just colour (sorry, I'm English!) the worksheets that you want to refer to, then do something like:
Sub ListSheets()
'lists only non-coloured sheets in immediate window
'(could amend to add to combo boxes)
Dim w As Worksheet
'loop over worksheets in active workbook
For Each w In Worksheets
If w.Tab.Color Then
'if tab color is set, print
Debug.Print w.Name
End If
Next w
Let me know if this solves your problem.