VBA - Highlight Cell With Checkbox - vba

Some logic to my process:
In column K on my worksheet I have inserted check boxes from cell K3 - K53 (this could become longer in the future) using the developer tab.
I then associated the check box with the same cell it is placed in.
I formatted the cells in this column by going to 'Format Cells', clicking on 'Custom' then typing in ';;;'. This was to HIDE the 'True/False' text from view.
My next step is to change the cell colour based on the text.
Note:
I have searched through a few forums and combined some code samples from them all, so I will not be able to reference the sources exactly, but below is what I have so far:
Code:
Sub Change_Cell_Colour()
Dim xName As Integer
Dim xChk As CheckBox
Dim rng As Range
Dim lRow As Long
lRow = ActiveWorksheet.Cells(Rows.Count, "B").End(xlUp).Row
Set rng = ActiveWorksheet.Range("K2:K" & lRow)
For Each xChk In ActiveSheet.CheckBoxes
xName = Right(xChk.Name, Len(xChk.Name) - 10)
If (Range(xChk.LinkedCell) = "True") Then
rng.Interior.ColorIndex = 6
Else
rng.Interior.ColorIndex = xlNone
End If
Next
End Sub
I keep getting an error on the line where I try to get the last row.
Code:
lRow = ActiveWorksheet.Cells(Rows.Count, "B").End(xlUp).Row
Error:
Object Required
I am not even sure if the code I have will solve my issue, so any help solving the main issue highlighting a cell based on the check box being checked or not, will be greatly appreciated.

Here's a quick rewrite with LOTS of comments explaining:
Sub Change_Cell_Colour()
Dim xChk As CheckBox
'Be explicit about which worksheet. Leaving it to "Activeworksheet" is going to cause problems
' as we aren't always sure which sheet is active...
'Also in this case we don't need to know the last row. We will iterate checkbox objects, not
' populate rows.
'lRow = ActiveWorksheet.Cells(Rows.Count, "B").End(xlUp).Row
'Again... we don't need this. We just need to iterate all the checkboxes on the sheet
'Set rng = ActiveWorksheet.Range("K2:K" & lRow)
'This is good stuff right here, just change the ActiveSheet to something more explicit
' I've changed this to the tab named "Sheet1" for instance.
For Each xChk In Sheets("Sheet1").CheckBoxes
'Getting the name of the checkbox (but only the last 10 characters)
xName = Right(xChk.Name, Len(xChk.Name) - 10)
'We can check the linked cell's value, but we can also just check if the
' if the checkbox is checked... wouldn't that be easier?
'If (Range(xChk.LinkedCell) = "True") Then
If xChk.Value = 1 Then
'Now we can use the "LinkedCell", but it's a STRING not a RANGE, so we will have
' to treat it as the string name of a range to use it properly
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = 6
Else
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = xlNone
End If
Next
End Sub
Here's the barebones version just to get it working
Sub Change_Cell_Colour()
Dim xChk As CheckBox
'Loop through each checkbox in Sheet1. Set it to color 6 if true, otherwise no color
For Each xChk In Sheets("Sheet1").CheckBoxes
If xChk.Value = 1 Then
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = 6
Else
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = xlNone
End If
Next
End Sub
I'm totally assuming here, but I would imagine you want this macro to fire when a checkbox is clicked. There is a handy Application.Caller that holds the name of the object that caused a macro to be called. You can set the "Assign Macro.." of each checkbox to this new code and then you can figure out which checkbox called the subroutine/macro using application.caller and follow the same logic to toggle it's linked cell color:
Sub Change_Cell_Colour()
Dim xChk As CheckBox
'Who called this subroutine/macro?
Dim clickedCheckbox As String
clickedCheckbox = Application.Caller
'Lets check just this checkbox
Set xChk = Sheets("Sheet1").CheckBoxes(clickedCheckbox)
'toggle its color or colour if you are a neighbour
If xChk.Value = 1 Then
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = 6
Else
Sheets("Sheet1").Range(xChk.LinkedCell).Interior.ColorIndex = xlNone
End If
End Sub

highlighting a cell based on the check box being checked or not
Select the sheet and apply a CF formula rule of:
=A1=TRUE

ActiveWorksheet doesn't exist, and because you haven't specified Option Explicit at the top of your module, VBA happily considers it an on-the-spot Variant variable.
Except, a Variant created on-the-spot doesn't have a subtype, so it's Variant/Empty.
And ActiveWorksheet.Cells being syntactically a member call, VBA understands it as such - so ActiveWorksheet must therefore be an object - but it's a Variant/Empty, hence, object required: the call is illegal unless ActiveWorksheet is an actual Worksheet object reference.
Specify Option Explicit at the top of the module. Declare all variables.
Then change ActiveWorksheet for ActiveSheet.

Related

VBA for performing action until cell meets criteria

I've had no luck on searching this, I hope you all can help.
I am trying to perform the following in Excel:
If a cell matches a value from table1, highlight that cell. Then highlight the cell below it and continue to highlight the cells below it until it reaches a cell that matches a value from table2.
I cant figure out how to work in the loop function for this
Thanks in advance
I've written a simple for loop, which iterates through the data range. My data table consists of integers 1 - 15, named 'Data'. The starting point is named 'Point_1' and the ending point is named 'Point_2'.
The code is below, annotated for clarity. In essence, it is simply toggling highlight ON when the cell matches Point_1, and highlighting until it is toggled back OFF, when the cell matches Point_2.
Sub HighlightRange()
Dim cel As Range
Dim dataRange As Range
Dim highlighting As Boolean
highlighting = False
With Application.ActiveWorkbook.Sheets("Sheet1")
Set dataRange = .Range("Data") 'This is your data range. I named mine 'Data'
For Each cel In dataRange
'Check for beginning or end values
If cel = .Range("Point_1").Value Then 'This is your starting value. I named mine 'Point_1'
highlighting = True
ElseIf cel = .Range("Point_2").Value Then 'This is your ending value. I named mine 'Point_2'
highlighting = False
End If
'While highlighting is activated, highlight the current cell
If highlighting = True Then
cel.Interior.ColorIndex = 5
End If
Next cel 'Check all cells in dataRange
End With
End Sub
Notably, the end point is not highlighted. However, you can move the ElseIf statement that turns highlighting back off to the end of the for loop, after the highlight command. This will turn highlighting off AFTER highlighting Point_2 instead of just before. Code for this case is below.
Sub HighlightRange()
Dim cel As Range
Dim dataRange As Range
Dim highlighting As Boolean
highlighting = False
With Application.ActiveWorkbook.Sheets("Sheet1")
'This code also highlights Point_2.
Set dataRange = .Range("Data") 'This is your data range. I named mine 'Data'
For Each cel In dataRange
'Check for beginning or end values
If cel = .Range("Point_1").Value Then 'This is your starting value. I named mine 'Point_1'
highlighting = True
End If
'While highlighting is activated, highlight the current cell
If highlighting = True Then
cel.Interior.ColorIndex = 5
End If
If cel = .Range("Point_2").Value Then 'This is your ending value. I named mine 'Point_2'
highlighting = False
End If
Next cel 'Check all cells in dataRange
End With
End Sub
I hope this helped! You can change the highlight color from blue (colorIndex = 5) to whatever color you like. If you click Record Macro and format a cell just how you want, you can copy the generated code into this macro in place of the following line:
cel.Interior.ColorIndex = 5
Cheers and Good Luck!

Add textbox value to it's destination

Situation: I'm working on a UserForm with the following Controls:
Combobox: This is used to pull up a list of names on Sheet2 (Column A) and allows the user to select a name that'll be used for the form.
TextBox: This is used to add a numerical value. That value will be placed on Sheet2, Column C, and two rows over from the name that's been selected from the combo box
CommandButton: This button is used to add the numerical value that has been typed into the text box into the cell on Sheet2, two columns over, and two rows over from the cell matching the name that's been choosen from the combobox
Problem: I have the Combobox and Textbox set up correctly but am having trouble creating VBA for the CommandButton to add the text box value to it's destination.
VBA So Far:
Private Sub AddButton_Click()
Dim WS As Worksheet
Dim Rng As Range
Dim Crystal As Long
Set WS = Worksheets("ParticipantList")
With WS.Range("a2:c300")
FindColumn = Application.WorksheetFunction.Match(Me.Participants.Value, WS.Range("A2:A300"), 1)
Crystal = Me.NumberOfCryst.Value
If FindColumn <> "" Then
With WS.Range("a2:c300")
Text = Me.NumberOfCryst.Value
WS.Activate
FindColumn = Application.WorksheetFunction.Match(Me.Participants.Value, WS.Range("A2:A300"), 0)
End With
End If
End With
End Sub
Now obviously this is all over the place and I've made tons of changes and attempts at getting it to work.
maybe you're after something like this:
Private Sub AddButton_Click()
Dim Rng As Range
Set Rng = Worksheets("ParticipantList").Range("A2:A300").Find(What:=Me.Participants.Value, LookIn:=xlValues, lookat:=xlWhole)
If Not Rng Is Nothing Then Rng.Offset(2, 2).Value = Rng.Offset(2, 2).Value + CLng(Me.NumberOfCryst.Text)
End Sub
you may also want to add some textbox text validation and be sure the user input a numeric value

populating data from userform checkboxes & optional buttons

I am creating a userform that I want to be able to populate values in a data tab as well as default to certain values.
I think I have text boxes and combo boxes down, but cannot find info on using multiple optional buttons to generate data to one cell depending on the selection.
from the example, my criteria would be "secondary insurance" how do I go about linking them all so that, lets say cell b1 is populated with the selected option?
I'm completely guessing but I think checkboxes are a little more simple, true if checked and false if unchecked.
What I have so far is just a code I came across to fill in a cell with the value of the designated text/combo box and was just going to repeat for each column I need to set a criteria for.
Private Sub CommandButton1_Click()
Dim LastRow As Long, ws As Worksheet
Set ws = Sheets("Sheet1")
LastRow = ws.Range("A" & Rows.Count).End(xlUp).Row + 1 'Finds the last blank row
ws.Range("A" & LastRow).Value = TextBox1.Text 'Adds the TextBox1 into Col A & Last Blank Row
Me.Hide
End Sub
combobox list
Private Sub UserForm_Initialize()
ComboBox1.Value = ("N/A")
ComboBox1.List = Split("N/A Yes No")
End Sub
Please let me know if I lack information and or how to attach my test worksheet, hopefully you can see the picture (I can't on my work server).
Thanks in advance for any and all education.
If the caption of the option button is the same as you want as cell text, then something like this may be what you want to store it:
Private Sub CommandButton1_Click()
Dim LastRow As Range
With Sheets("Sheet1")
Set LastRow = .Rows(.Cells(Rows.Count, 1).End(xlUp).Row + 1).Cells
If OptionButton1 Then
LastRow(2).Value2 = Me.OptionButton1.Caption
ElseIf Me.OptionButton2 Then
LastRow(2).Value2 = Me.OptionButton2.Caption
Else
LastRow(2).Value2 = Me.OptionButton3.Caption
End If
End With
End Sub
This will set the desired cell to the value of the caption of the option button you have selected.
To load the data back in the userform, you could use something like this:
Sub Load_in(Row_To_Load As Long)
Dim MyRow As Range
With Sheets("Sheet1")
Set MyRow = .Rows(Row_To_Load).Cells
If MyRow(2).Value2 = OptionButton1.Caption Then
OptionButton1.Value = True
ElseIf MyRow(2).Value2 = OptionButton2.Caption Then
OptionButton2.Value = True
Else
OptionButton3.Value = True
End If
End With
End Sub
For this, I assumed that the names hasn't been changed. Also if nothing is selected, the third option (N/A) will be used. The same goes for loading it back. If you do not want that, simply change the Else part to ElseIf so it looks like the first 2 options.

Detect change from nested formulas

I have a very complex workbook with many tabs. The tabs may have either normal data or formulas in various cells. In the case of formulas, the formulas may be nested from one sheet to the next (i.e. a formula on sheet1 refers to a formula on sheet2 which in turn refers to a formula on sheet3, etc.).
I have a hidden tab that contains the following: source sheet, source range, target sheet, and target range.
A named range has been created over these 4 fields and all applicable rows.
When we wish to save data to the database, we loop through every row in the range mapping and copy the data from the source sheet/range to the target sheet/range. After this, the applicable data is serialized into XML and sent to a web service to be saved.
The problem that we wish to resolve is that we want to mark a cell on a hidden sheet when a change is made by the user to a source range. Since formulas can be nested, the Worksheet_Change event does not pick up the change.
Since a change on one sheet may affect another sheet that is not the active sheet, the Workbook_SheetChange event does not catch the change either.
Is there any way form me to catch when a sheet defined in the mapping is changed, even if it is the result of a formula change several levels deep?
Edit
Thank you for your responses. I was attempting to find the fastest and least process intensive way to determine if data changes within a monitored range. The data may consist of actual data or of nested formulas.
My research showed that I could not actually achieve this result by taking range intersections as I could not detect if the data within a monitored range was modified. This is due to the fact that the monitored range may not be on the active sheet and also may contain formulas.
I have shown the method used to actually detect a change below. If there is any feedback on a better way to achieve the same result, I would appreciated it.
Worksheet_Change event will not work if a cell value is changed by a formula, you need Worksheet_Calculate.
Check out my example workbook here.
And Here for the WebPage of example codes
There is no "easy" way to detect if a nested formula has changed when the formula being monitored is not on the active sheet. While my hope was to detect the modified range and use an intersection of ranges to set a flag, this was not possible because the Worksheet_Change event does not work on formulas and the Workbook_SheetChange event only works on the active sheet. Since my workbooks have over 20+ tabs and 20 - 30 ranges being monitored, this approach does not work. This approach was desired for speed purposes.
Instead, the workbook will need to "check" to see if the current values are the same as the last time the save to database event was called. If not, a dirty flag will be set.
The code for this approach is provided below.
An example of the mapping range is shown in the picture below though in practice there are 20-30 rows comprising this range.
There are three other sheets where Sheet3 contains actual data in A1:H1 and Sheet2 has formulas pointing to Sheet3. Sheet1 has formulas pointing to Sheet2.
As the mapping range indicates, we are looking at a range on Sheet1, even though changes may be made to Sheet3.
The code used is as provided below.
Option Explicit
Public Sub DetermineIfEditOccurred()
Dim oMappingRange As Range
Dim szSourceTab As String
Dim szSourceRange As String
Dim oSourceRange As Range
Dim szTargetTab As String
Dim szTargetRange As String
Dim oTargetRange As Range
Dim oWorksheetSource As Worksheet
Dim oWorksheetTarget As Worksheet
Dim oRangeIntersection As Range
Dim nRowCounter As Long
Dim nCellCounter As Long
Dim szSourceValue As String
Dim szTargetValue As String
Dim oCell As Range
Dim bIsDirty As Boolean
If Range(ThisWorkbook.Names("DirtyFlag")).Value = 0 Then
Set oMappingRange = Range(ThisWorkbook.Names("Mapping"))
For nRowCounter = 1 To oMappingRange.Rows.Count
szSourceTab = oMappingRange(nRowCounter, 1)
szSourceRange = oMappingRange(nRowCounter, 2)
szTargetTab = oMappingRange(nRowCounter, 3)
szTargetRange = oMappingRange(nRowCounter, 4)
Set oWorksheetSource = ThisWorkbook.Worksheets(szSourceTab)
Set oWorksheetTarget = ThisWorkbook.Worksheets(szTargetTab)
Set oSourceRange = oWorksheetSource.Range(szSourceRange)
Set oTargetRange = oWorksheetTarget.Range(szTargetRange)
nCellCounter = 1
For Each oCell In oSourceRange.Cells
szSourceValue = oCell.Value
If szSourceValue = "#NULL!" Or _
szSourceValue = "#DIV/0!" Or _
szSourceValue = "#VALUE!" Or _
szSourceValue = "#REF!" Or _
szSourceValue = "#NAME?" Or _
szSourceValue = "#NUM!" Or _
szSourceValue = "#N/A" Then
szSourceValue = ""
End If
szTargetValue = GetCellValueByPosition(oTargetRange, nCellCounter)
If szSourceValue <> szTargetValue Then
Range(ThisWorkbook.Names("DirtyFlag")).Value = 1
bIsDirty = True
Exit For
End If
nCellCounter = nCellCounter + 1
Next
If bIsDirty Then
Exit For
End If
Next
End If
End Sub
Public Function GetCellValueByPosition(oRange As Range, nPosition As Long) As String
Dim oCell As Range
Dim nCounter As Long
Dim szValue As String
nCounter = 1
For Each oCell In oRange
If nCounter = nPosition Then
szValue = oCell.Value
Exit For
End If
nCounter = nCounter + 1
Next
GetCellValueByPosition = szValue
End Function
The Workbook_SheetChange event is as follows:
Option Explicit
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call DetermineIfEditOccurred
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Sh.Name <> "MAPPING" Then
Call DetermineIfEditOccurred
End If
End Sub

Does there exist a VBA command which does not change the formula of a cell, but its value?

Suppose in a worksheet the formula of R4 cell is =B1+B2, and its current value is 10.
A VBA command Range("R4").Value = 5 will change both its formula and its value to 5.
Does anyone know if there exists a VBA command which changes the value of R4 to 5, but does not change its formula, such that its formula is still =B1+B2?
PS: we can also achieve the same state in another way: 1) do a Range("R4").Value = 5 2) change the formula of R4 to =B1+B2 but without evaluating it. In this case, does there exist a VBA command which change the formula of a cell without evaluating it?
Edit: What I want to do is...
I would like to write a function, which takes a worksheet where some cells may be out of date (the formula does not match its value), and generates automatically a VBA Sub, this VBA Sub can reproduce this worksheet. The VBA Sub may look like:
Sub Initiate()
Cells(2,3).Value = 5
Cells(4,5).Value = 10
...
Cells(2,3).Formula = "=2+3"
Cells(4,5).Formula = "=C2+C2"
...
End Sub
Such that running Initiate() builds one worksheet with same values and formulas.
Without the VBA command I am asking, this Initiate() will be hard to generated.
You cannot change the value of a cell to something different than what the cell formula computes to.
Regarding your p.s.: You can probably change the formula of a cell without re-evaluation by changing the calculation mode to manual. But that would of course apply to the entire workbook, not just this one cell
EDIT: maybe a solution would be to temporarily save the formula of the cell in either a tag of that cell, or a hidden worksheet?
It is quite simple to change the result of a formula without changing the formula itself:
Change the value of of its argument(s). This is a Solver-type approach:
Sub ForceDesiredResult()
Dim r As Range
Set r = Range("B2")
With r
If r.HasFormula Then
.Formula = .Formula & "-5"
Else
.Value = .Value - 5
End If
End With
End Sub
Here is some very dirty code that will save all values of all formulas on the active sheet as custom properties of the sheet, and a 2nd sub that will mark red all cells where the value has changed from it's original value, while preserving all formulas. It will need some error-checking routines (property already exists, property doesn't exist,...) but should give you something to work with. Since I don't really understand your problem it's a bit hard to say ;)
Sub AddCustomProperty()
Dim mysheet As Worksheet
Dim mycell2 As Range
Dim myProperty As CustomProperty
Set mysheet = ActiveWorkbook.ActiveSheet
For Each objcell In mysheet.UsedRange.Cells
Debug.Print objcell.Address
If objcell.HasFormula Then Set myProperty = mysheet.CustomProperties.Add(objcell.Address, objcell.Value)
Next objcell
End Sub
Sub CompareTags()
Dim mysheet As Worksheet
Dim mycell2 As Range
Dim myProperty As CustomProperty
Set mysheet = ActiveWorkbook.ActiveSheet
For Each objcell In mysheet.UsedRange.Cells
Debug.Print objcell.Address
If objcell.HasFormula Then
On Error Resume Next
If mysheet.CustomProperties(objcell.Address).Value <> objcell.Value Then
objcell.Font.ColorIndex = 3
On Error GoTo 0
End If
End If
Next objcell
End Sub