Excel Data Validation Combo box Click - vba

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.

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.

Prevent sub from running

In sheet1 (which I've Called "MainSheet") I have a sub in my VBA script that check the values of some cells whenever a cell is changed in this sheet. (one of the main actions that will occur when a cell is changed is modifying it's color, green for Cell's with a value, red for empty cells)
But now I've got some other sub's that also change cells (in the main sheet) but in this case I don't need (and don't want) VBA to check the cells and adapt the color to their values after every cell change. (annoying when editing a large amount of cells).
(I've already tried to put this sub in the "ThisWorkbook"part of VBA instead of the Sheet1(MainSheet) part, but unfortunately this made no difference at all).
Question one: is it possible to prevent this?
I also have a correlated problem with another sub that worth mentioning in the same question I think: In this sub a new sheet is created, named and filled with text from a .txt document. Then the sheet will be saved as new workbook, and the sheet will be deleted. (The name of the sheet equals the name it will get when it's saved, and varies ever new occurrence.)
When I'm copying the .txt lines into this sheet one by one, the first sub I mentioned (the one editing cell color) is called. one of the first things happening in this sub is calling my MainSheet. When thin sub is finished the line copying sub will continue but will start pasting the lines in my Main Sheet. I tried to enter lines in this sub that select the sheet with variable name, but it keeps jumping to the MainSheet.
Question two: How do I prevent jumping to the MainSheet?
(Both questions probably could have the same solution.)
The sub that modifies the cell colours:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim j As Integer
'Collor all cells green containing values, collor empty cells red.
''Starts automaticly after every cell change within this sheet
'Huidige Cell onthouden
If Not Intersect(Target, Range("A9:A29")) Is Nothing Then
On Error GoTo bm_Safe_Exit3
Application.EnableEvents = False
If Intersect(Target, Range("A9:A29")).Cells.Count > 1 Then
Application.Undo
MsgBox "Please edit one cell at a time!"
Else
Dim newVal3 As Variant
newVal3 = Target.Value
Range("A9:A29").ClearContents
Target.Value = newVal3
End If
End If
bm_Safe_Exit3:
Application.EnableEvents = True
Set myActiveCell = ActiveCell
Set myActiveWorksheet = ActiveSheet
Set myActiveWorkbook = ActiveWorkbook
Sheets("MainSheet").Select
Range("C5").Select
j = 0
Do While j < 6
If ActiveCell.Offset(0, j).Value = "" Then
ActiveCell.Offset(-1, j).Interior.Color = RGB(255, 0, 0)
Else: ActiveCell.Offset(-1, j).Interior.Color = RGB(0, 255, 0)
End If
j = j + 1
Loop
'Terug naar de voormalig active cell
myActiveWorkbook.Activate
myActiveWorksheet.Activate
myActiveCell.Activate
End Sub
Using .Select and .Activate is inefficient at the best of times; in a Worksheet_Change event macro it can really foul the waters.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo bm_Safe_Exit
Application.EnableEvents = False
If Not Intersect(Target, Range("A9:A29")) Is Nothing Then
If Intersect(Target, Range("A9:A29")).Cells.Count > 1 Then
Application.Undo
MsgBox "Please edit one cell at a time!"
'intentionally throw an error; no more code run; sent to bm_Safe_Exit
Err.Raise 0
Else
Dim newVal3 As Variant
newVal3 = Intersect(Target, Range("A9:A29")).Cells(1).Value
Range("A9:A29").ClearContents
Intersect(Target, Range("A9:A29")).Cells(1) = newVal3
End If
End If
Dim j As Integer
With Worksheets("MainSheet").Range("C5")
For j = 0 To 6
If Not CBool(Len(.Offset(0, j).Value)) Then
.Offset(-1, j).Interior.Color = RGB(255, 0, 0)
Else
.Offset(-1, j).Interior.Color = RGB(0, 255, 0)
End If
Next j
End With
bm_Safe_Exit:
Application.EnableEvents = True
End Sub
It isn't clear exactly what worksheet this is running under; I hope it isn't the MainSheet as I've used direct referencing to the cells on that worksheet.
See How to avoid using Select in Excel VBA macros for more methods on getting away from relying on select and activate to accomplish your goals.

how to read the value from a range of comboboxes?

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.

VBA (Excel): Jump to (or remain) cell in column

I would like to start off with stating that I have virtually no coding experience. I found a VBA snippet online for highlighting an entire selected range (just to as a visual guide):
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
Application.ScreenUpdating = False
' Clear the color of all the cells
Cells.Interior.ColorIndex = 0
With Target
' Highlight the entire column that contain the active cell
.EntireRow.Interior.ColorIndex = 8
End With
Application.ScreenUpdating = True
End Sub
I would like to also have the cursor jump-to column "J". For instance, after performing a search for cells containing the words "strawberry topping" after pressing 'OK' the cell containing that text becomes active and, due to the VBA code, the entire row is highlighted.
The first cell I need to work on is in column "J". Can I also have column J selected along with the row being highlighted?
Thank you so much for your time and would appreciate any help you may have to offer.
My Three cents
If you are using xl2007+ then do not use Target.Cells.Count. Use Target.Cells.CountLarge else you will get an Overflow error if a user tries to select all cells by pressing CTRL + A as Target.Cells.Count can't hold a Long value.
If you want to select the row and the column, you might want to switch off events else you might end up in endless loop.
Since you are working with events, use error handling.
Is this what you are trying?
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim Rw As Long, Col As Long
Dim ColName As String
On Error GoTo Whoa
If Target.Cells.CountLarge > 1 Then Exit Sub
Application.ScreenUpdating = False
Application.EnableEvents = False
' Clear the color of all the cells
'Cells.Interior.ColorIndex = 0
With Target
Rw = .Row
Col = .Column
ColName = Split(Cells(, Col).Address, "$")(1)
' Highlight the entire column that contain the active cell
'.EntireRow.Interior.ColorIndex = 8
Range(ColName & ":" & ColName & "," & Rw & ":" & Rw).Select
End With
LetsContinue:
Application.ScreenUpdating = True
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
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.