how to read the value from a range of comboboxes? - vba

I have an Excel sheet which contains 40 combobox.
I have a worksheet_change event which has to be executed, only when these 40 comboboxes have a specified value.
I have this code (which I found here: Get the selected value of a activex combobox using vba) to read the value of a combobox.
I think I will be able to make a loop to read each combobox.
But my problem is setting the rule that uses the value for the event handler.
Can I create something (a variable) that is 1 or 0 whether or not the criteria is matched?
i have this code for reading the value of a combobox:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet
Dim cboCorpConsumer As ComboBox
Dim a As String
Dim i As Integer
Set ws = Worksheets("simulator VER")
Set cboCorpConsumer = ws.OLEObjects("ComboBox2).Object
How could I achieve the above? Or should I use a rule for each combobox?
if combobox2.value = 1 then
if combobox3.value = 1 then
etc. etc.
if combobox40.value = 1 then execute event handler
Edit 2:
Thank you David Zemens. Your answer helped me to adjust it and make it work for my specific issue. this is the code I have now which works fine! (And I used this topic: Call a function when only a specific Excel cell changes on Formula Recalculation to make sure the code is only run when 2 specific cells are changed).
Private Sub Worksheet_Change(ByVal Target As Range)
Dim objOLE As OLEObject
Dim cb As ComboBox
Dim a As String
Dim i As Integer
Application.ScreenUpdating = False
If Target.Address = Sheets("simulator VER").Range("E6").Address Then
'## Make sure ALL comboboxes have the value of "1" before proceeding
For Each objOLE In Me.OLEObjects
If TypeName(objOLE.Object) = "ComboBox" Then
Set cb = objOLE.Object
If cb.Value <> "No Promotion" Then GoTo Earlyexit
End If
Next
'### The rest of your procedure code goes here:
With Worksheets("Simulator VER")
.Range("O21:O60").Copy
.Range("P21:P60").PasteSpecial Paste:=xlPasteValues
End With
Else
If Target.Address = Sheets("simulator VER").Range("E7").Address Then
'## Make sure ALL comboboxes have the value of "1" before proceeding
For Each objOLE In Me.OLEObjects
If TypeName(objOLE.Object) = "ComboBox" Then
Set cb = objOLE.Object
If cb.Value <> "No Promotion" Then GoTo Earlyexit
End If
Next
'### The rest of your procedure code goes here:
With Worksheets("Simulator VER")
.Range("O21:O60").Copy
.Range("P21:P60").PasteSpecial Paste:=xlPasteValues
End With
Else
End If
End If
Earlyexit:
Application.ScreenUpdating = True
End Sub
Thank you!

If all comboboxes must have the specified value to allow the _Change event to fire, you can do something like this:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim objOLE as OLEObject
Dim cb As ComboBox
Dim a As String
Dim i As Integer
'## Make sure ALL comboboxes have the value of "1" before proceeding
For each objOLE in Me.OLEObjects
If TypeName(objOLE.Object = "ComboBox") Then
Set cb = objOLE.Object
If cb.Value <> 1 Then GoTo EarlyExit
End If
Next
'### The rest of your procedure code goes here:
EarlyExit:
End Sub
Note: technically the _Change event fires every time, but the GoTo EarlyExit aborts the procedure before the rest of your code is executed.

Related

Worksheet Change to run macro

My workbook contains several sheets, each with multiple checkboxes. All checkboxes in all worksheets have the linked cell in row 80. In a worksheet called "Info" I am using countif to count the total number of times the text "TRUE" occurs in row(s) 80 for all worksheets. The total is in Info!B8.
I need to call a macro each time cell Info!b8 changes. So in other words; every time a checkbox is clicked, the linked cell changes, cell Info!b8 goes up or down and I need a macro to run.
This is the code I am using, but it doesn't do anything. I have researched this and from what I can tell it should work??
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$B$8" Then
Call CreateFinalWorksheet
End If
End Sub
Assuming all your CheckBoxes are of Form Controls, with a bit of altering the CheckBox Creation, you can achieve what you want without the need of LinkedCell and CountIfs etc.
Example: CreateCheckBoxes() below will create a check box for each cell in Range("D1:D5"), Name it with a prefix and the cell address, assigns the Sub CheckBoxClicked when clicked.
In Sub CheckBoxClicked(), it will go through all worksheets (except named "Info"), then increment a counter if the value of checkbox named ending D3 is 1 (ticked). After that, if threshold is met, it calls the Sub CreateFinalWorksheet().
Option Explicit
Private Const ChkBoxPrefix As String = "cbx_"
Private Const ThresholdToCreateFinalWorksheet As Long = 3
Sub CreateChkBoxes()
Dim myCBX As CheckBox, c As Range
For Each c In Range("D1:D5") 'rngCB
With c
Set myCBX = ActiveSheet.CheckBoxes.Add(Top:=.Top, Width:=.Width, Height:=.Height, Left:=.Left)
End With
With myCBX
.Name = ChkBoxPrefix & c.Address(0, 0)
.Caption = "Check Box " & c.Address(0, 0) 'strCap
.OnAction = "CheckBoxClicked" ' "CheckBox_Click"
End With
Next c
End Sub
Sub CheckBoxClicked() ' CheckBox_Click()
Dim oWS As Worksheet, lChecked As Long
On Error Resume Next ' Just in case the named CheckBox does not exist
lChecked = 0
For Each oWS In ThisWorkbook.Worksheets
If oWS.Name <> "Info" Then
' If you need to keep track of more than 1 checkbox in each worksheet, go through them
' If you need all of them to be checked before CreateFinalWorksheet, exit when a checkbox.value = 0
With oWS.CheckBoxes(ChkBoxPrefix & "D3") ' <-- Change to what you need to keep track of
lChecked = lChecked + IIf(.Value = 1, 1, 0)
End With
End If
Next
On Error GoTo 0
If lChecked >= ThresholdToCreateFinalWorksheet Then CreateFinalWorksheet
End Sub
Private Sub CreateFinalWorksheet()
Debug.Print "CreateFinalWorksheet()"
End Sub
Alternatively you put the event Sub Worksheet_Calculate() into Info module, and check if the Info!B8 is large enough to call CreateFinalWorksheet.

Use cell value as range to hide columns

I have a spreadsheet that there is a checkbox the purpose of the checkbox is to hide the name of clients in two adjacent columns. Because the spreadsheet changes from time to time the position of the columns changes thus it is currently P:Q but a year ago it was H:I.
I want to store the 'range' in a cell and reference that from my vba and get that to hide the columns. The checkbox is a simple toggle. I have tried various incarnations without success and my latest effort tells me that I have not se up the range properly. The cel I am using for teh range is F4. The code is currently:
Private Sub CheckBox2_Click()
Dim c As Range
Dim Visy As Integer
Dim My_range As String
'My_range is the range of filled rows stored as a range in cell F4
'Visy stores the state of the checkbox
If CheckBox2.Value = True Then
Visy = 1
Else
Visy = 0
End If
'Stop any use of the spread sheet and set variable initial states
Application.EnableEvents = False
My_range = Sheet9.Cells(4, 6).Value
'Hide the columns
Range(My_range).Hidden = Visy
'Sheet9.colums(My_range).Hidden = True
'Re enable application
On Error GoTo 0
Application.EnableEvents = True
End Sub
This is within a single sheet:
Sub qwerty()
My_range = Cells(4, 6).Value
Range(My_range).EntireColumn.Hidden = True
End Sub
Your Private Sub CheckBox2_Click should be in a worksheet's code sheet. I believe this is the worksheet identified by the Sheet9 worksheet .CodeName property.
A Private Sub in a worksheet codesheet does not have to explicitly reference the .Parent worksheet property on any Range object or
Range.Cells object unless you want to reference another worksheet's cells. These are bound to the cells on the worksheet whose codesheet you are on regardless of the ActiveSheet property.
Private Sub CheckBox2_Click()
Range(Cells(4, "F").Text).EntireColumn.Hidden = CBool(Me.Value)
End Sub
Do not confuse a worksheet's Private Sub behavior with a Private Sub on a module code sheet. A module codesheet should always explicitly reference the parent worksheet (and often the parent workbook) regardless of whether the Sub is Public or Private.
You have to use code in context:
Private Sub CheckBox2_Click()
Dim wsh As Worksheet
Dim sRangeName As String
'context!
Set wsh = ThisWorkbook.Worksheets("TypeNameHere")
sRangeName = wsh.Range("F4")
wsh.Range(sRangeName).EntireColumn.Hidden = CheckBox2.Value
Set wsh = Nothing
End Sub
Thanks to all who responded it helped a lot and put me on the right track. As several of you noted context is important and I was mixing private sub and sub and so had a scope problem when it came to ranges. I also from another source had the suggestion to use a named range rather than read a cell value since the columns were always adjacent. I have published the code below in case it is of value to anyone in the future.
Private Sub CheckBox2_Click()
'Requires ClientNameCol to be set to the range to be hidden
Dim Visy As Boolean
'Stop any use of the spread sheet and set variable initial states
Application.EnableEvents = False
'Check if sheet is to be hidden or not
If Worksheets("Client 16").CheckBox2.Value = True Then
Visy = True
Else
Visy = False
End If
'Hide/unhide the columns
With ThisWorkbook
.Worksheets("Client 16").Range("ClientNameCol").EntireColumn.Hidden = Visy
End With
On Error GoTo 0
Application.EnableEvents = True
End Sub

Referencing Most Recently Added Worksheet

I have a userform that fields the user's input to take certain actions within a workbook, one of the actions is inserting a new tab in the workbook and having the user input the new sheet's name within an input box. I want to be able to then reference this new sheet (but I won't know what someone else might name it) and to paste a chart object within the newly created sheet.
So far the adding sheet code is working fine, but any of my attempts to paste the chart range are not working. My current code for adding the worksheet is:
Private Sub MyChart_Click()
Dim Answer As String
Dim sht_name As Variant
On Error Resume Next
If Me.OptionButton2.Value = True Then
Unload Me
sht_name = InputBox("Please enter value")
If sht_name <> "" Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = sht_name
Else
Exit Sub
End Sub
My chart lives in another worksheet ("Sheet2") and I am trying to just copy it into the newly created sheet whenever the user selects this OptionButton2 in the Userform... Any help is appreciated.
When you use the Worksheets.Add method, that sheet automatically is activated. To test this you can run this small portion of code:
Option Explicit
Private Sub SheetReference()
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = "Test"
Debug.Print ActiveSheet.Name
End Sub
And the output you would see is
Test
So in your case, you could declare a worksheet variable and then set the reference after you call the add method. Something like this:
Option Explicit
Private Sub MyChart_Click()
Dim Answer As String
Dim sht_name As Variant
Dim ws As Worksheet
On Error Resume Next
If Me.OptionButton2.Value = True Then
Unload Me
sht_name = InputBox("Please enter value")
If sht_name <> "" Then
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = sht_name
Set ws = ActiveSheet
With ws
'Do whatever you need to do on the worksheet
End With
Else
Exit Sub
End If
End Sub

Excel Data Validation Combo box Click

I am very new to coding...
Sheet 1 has my data validation in a scroll list on it and sheet 2 has the list that it is validating the data from. I am trying to make a combo box on sheet 1 that will auto fill while you type instead of having to type the exact name. The code below only works if the data list is on the same sheet as the sheet I am trying to make the combo box on. Any idea how to change the code so it will pull from sheet 2 where all the list of data is located?
Any help would be greatly appreciated
'=========================================
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim str As String
Dim cboTemp As OLEObject
Dim ws As Worksheet
Set ws = ActiveSheet
On Error GoTo errHandler
If Target.Count > 1 Then GoTo exitHandler
Set cboTemp = ws.OLEObjects("TempCombo")
On Error Resume Next
If cboTemp.Visible = True Then
With cboTemp
.Top = 10
.Left = 10
.ListFillRange = ""
.LinkedCell = ""
.Visible = False
.Value = ""
End With
End If
On Error GoTo errHandler
If Target.Validation.Type = 3 Then
'if the cell contains a data validation list
Application.EnableEvents = False
'get the data validation formula
str = Target.Validation.Formula1
str = Right(str, Len(str) - 1)
With cboTemp
'show the combobox with the list
.Visible = True
.Left = Target.Left
.Top = Target.Top
.Width = Target.Width + 15
.Height = Target.Height + 5
.ListFillRange = ws.Range(str).Address
.LinkedCell = Target.Address
End With
cboTemp.Activate
'open the drop down list automatically
Me.TempCombo.DropDown
End If
exitHandler:
Application.ScreenUpdating = True
Application.EnableEvents = True
Exit Sub
errHandler:
Resume exitHandler
End Sub
'====================================
'Optional code to move to next cell if Tab or Enter are pressed
'from code by Ted Lanham
'***NOTE: if KeyDown causes problems, change to KeyUp
Private Sub TempCombo_KeyDown(ByVal _
KeyCode As MSForms.ReturnInteger, _
ByVal Shift As Integer)
Select Case KeyCode
Case 9 'Tab
ActiveCell.Offset(0, 1).Activate
Case 13 'Enter
ActiveCell.Offset(1, 0).Activate
Case Else
'do nothing
End Select
End Sub
'====================================
You have a lot of code just to populate a Combobox in the active sheet.
Since you are using the Worksheet_Change event, you don't have to set the worksheet as the active sheet, it already is.
This sample code will populate ComboBox1 with a range of cells from Sheet2,
The code and Combobox1 are located in Sheet1.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim ws As Worksheet, Rws As Long, Rng As Range
'ComboBox1 and this code are in Sheet1
Set ws = Sheets("Sheet2") 'sheet2 column 1 is the list to populate Combobox1
With ws 'set the list range
Rws = .Cells(Rows.Count, "A").End(xlUp).Row
Set Rng = .Range(.Cells(1, 1), .Cells(Rws, 1))
End With
With ComboBox1 'populate the combobox
.Clear
.List = Rng.Value
End With
End Sub
If you can forgo the need to have Excel autocomplete based on a user's entry, you can use the cell validation as demonstrated here (backward compatible with older versions):
Dependent Drop-down Lists
Or here (for newer versions):
Insert or delete a drop-down list
The upside to this is that it requires no code, unless you would like to manipulate the list automatically.
Using a combobox object is definitely more complicated, so you might skip on that until your coding catches up to the complexity required by objects.
==EDIT==
OK, then, 2 things: this code (Private Sub Worksheet_SelectionChange(ByVal Target As Range)) applies every time you click a new cell. If your values don't change very much you can make sure the cbo only populates on workbook_open, or something less active. However, if the combobox source data changes frequently while the workbook is open then having a frequent refresh to the source would be worthwhile.
Try naming the range you want the combobox to get its data from and pointing the ListFillRange to that named range, like:
.ListFillRange = "myNamedRange"
Then, if your source grows or shrinks, incorporate code that redefines the named range based on the changes.

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.