Changing Target.Address location based on iterations vba - excel

I have code that inserts rows based on a cell value in cell J17 using Target.Address. I'd like to use the similar code on the line below it; however, the location of the line below it is dependent on the number of rows that are added.
Does anyone know of a method to add the number of iterations to the rows of an address?
I know how to do this using Cells function, but I like the automatic running of the Target.Address
Here is my code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$J$17" Then
Dim i As Integer
For i = 1 To Target.Value
Cells(18, 9).EntireRow.Insert
Cells(18, 9).Value = i
Next i
End If
End Sub

Does anyone know of a method to add the number of iterations to the rows of an address?
Sure. That part is elementary. You could compute the range's new address using the Offset method. But then you need to store it somewhere. You could create a CustomDocumentProperty, a hidden worksheet or a named range variable. All of these would work just fine.
But probably the best solution is just to define that cell initially as a named range. Named ranges are neat because they are dynamic. If you insert rows/columns or delete rows/columns, the Name moves with the cell. Then, in your code, just compare versus that range's .Address property, instead of hard-coding in a value like "$J$18".
Here's how:
First, define a named range for cell J18. I named it "insert", but you can name it (almost) anything you want. Just make sure to update the reference to it in the code.
Use something like this if you are going to do different operations depending on which Target is changing.
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
'This handles your original J17
If Target.Address = "$J$17" Then
For i = 1 To Target.Value
Cells(18, 9).EntireRow.Insert
Cells(18, 9).Value = i
Next i
End If
'This handles the new named range:
If Target.Address = Range("insert").Address Then
'Do something else, here:
MsgBox "Success!!"
End If
Application.EnableEvents = True
End Sub
If you are doing the same insertion and numbering, then I would fancy it up like this:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
With Target
If .Address = "$J$17" Or .Address = Range("insert").Address Then
For i = 1 To .Value
.Offset(1, -1).EntireRow.Insert
.Offset(1, -1).Value = i
Next i
End If
End With
Application.EnableEvents = True
End Sub

Related

Using VBA Workbook_SheetChange only works in one direction

I am trying to use the Workbook_SheetChange feature of excel. I want to have multiple worksheets feed information into a master worksheet, and if you update a cell in a source sheet, the corresponding cell in the master sheet will also change, and vice versa.
Currently, I am starting small with just trying to get this thing to work with one cell so I can build on this. The code I have works, but only in one direction; when I edit something in the source sheet, the cell value in the master sheet changes to that new value. However, when I try to change the value in the master sheet, the value in the master sheet kinda bounces around until it finally decides to stick to the value derived from the source sheet. This only occurs whenever I try to have the cell portion of the address be the same between the two sheets; if the target cell in Sheet1 is $A$1 and the target cell in Sheet2 is any cell that is not $A$1, then there are no issues. This issue only occurs if the cell in both sheets is the same.
Below is the code that I am currently using.
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
cell_1 = Worksheets("Sheet1").Range("$R$3").Address
cell_2 = Worksheets("Sheet2").Range("$R$3").Address
If Target.Address = cell_1 Or Target.Address = cell_2 Then
Call cellUpdate(Target.Address)
End If
End Sub
Sub cellUpdate(Target As String)
cell_1 = Worksheets("Sheet1").Range("$R$3").Address
cell_2 = Worksheets("Sheet2").Range("$R$3").Address
Application.EnableEvents = False
With ActiveWorkbook
If Target = cell_1 Then
Worksheets("Sheet2").Range("$R$3").Value = Worksheets("Sheet1").Range(Target)
ElseIf Target = cell_2 Then
Worksheets("Sheet1").Range("$R$3").Value = Worksheets("Sheet2").Range(Target)
End If
End With
Application.EnableEvents = True
End Sub
How do I get around this issue? I couldn't find any information online on how to avoid this since the uses I found for this Workbook_SheetChange function are for things that occur in one sheet rather than multiple sheets.
What is happening here is that you extensively use Target.Address. The issue with this is that Target.Address only returns the cell address, not the sheet it is on. For example it would return $A$1. Not Sheet1!$A$1. This means that in your if statement it tests whether "$A$1" = "$A$1" regardless of which sheet this address is on. Therefore it will only ever run the first clause of this loop resulting in it only working one way.
Secondly, you have a lot of redundant code, hard-coding a bunch of addresses multiple times. This can be massively simplified as demonstrated below:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim cell_1, cell_2 As Range
Set cell_1 = Worksheets("Sheet1").Range("$R$3")
Set cell_2 = Worksheets("Sheet2").Range("$R$3")
Application.EnableEvents = False
If Target = cell_1 Then
cell_2.Value = cell_1.Value
ElseIf Target = cell_2 Then
cell_1.Value = cell_2.Value
End If
Application.EnableEvents = True
End Sub
This code uses the first and second cell as range which stores the entire cell in memory, address, values, the lot. It then switches off EnableEvents as you did (good effort by the way, to prevent yourself from getting stuck in an infinite loop as most people would with this kind of code). Then it checks whether your target cell is cell 1 and switches the value of cell two with cell one, and the same for cell 2. No need for a separate function.
Couple of observations:
Worksheets("Sheet1").Range("$R$3").Address will always return the string $R$3, as will Worksheets("Sheet2").Range("$R$3").Address.
Both lines return exactly the same thing - it doesn't care what sheet it's on.
The Call statement isn't required.
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Application.EnableEvents = False
'Address is R3 on whichever sheet you're changing.
If Target.Address = "$R$3" Then
'Figure out which sheet was changed and update as required.
Select Case Sh.Name
Case "Sheet1"
Worksheets("Sheet2").Range("R3") = Sh.Range("R3")
Case "Sheet2"
Worksheets("Sheet1").Range("R3") = Sh.Range("R3")
Case "Sheet3", "Sheet4"
'Do stuff if you're on sheet 3 or sheet 4.
Case Else
'Do stuff if you're on any other sheet.
End Select
End If
Application.EnableEvents = True
End Sub

How can a format an entire row based on if a cell CONTAINS a specific word using vba?

I need to highlight and entire row if a cell string contains the word "risk". But I need to make it using vba since the person using it will write on it after using the macro.
I have something like:
The reason will be written afterwards and I need to highlight the row if someone writes the word risk anywhere in this column. Anything can be written there.
I use this to highlite a row when I want a full match:
lastReg= Cells(Rows.Count, 1).End(xlUp).Row
Set Rng = Range("A1:J" & lastReg)
Rng.FormatConditions.Add Type:=xlExpression, Formula1:="=$J1=""Plat"""
...
so I tried:
Rng.FormatConditions.Add Type:=xlExpression, Formula1:="=FIND(""risk"",$J1)>0"
But it doesn't work.
Edit: it gives me an execution error so the code itself doesn't run.
Edit2: Someone else uses this macro, and he can't do it by himself so I wanted the code to do it for him.
Also, the code is stored in the personal.xlsb because he runs the code in a different worksheet everyday, so I can't pre config the formatConditions for the worksheet.
I would use a worksheet change event. place this sub in your worksheet. Whenever any cell in column 5 changes and the value is "risk", it will color the row.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Rng As Range
Set Rng = Columns(5)
If Not Intersect(Rng, Target) Is Nothing And Target.Value = "risk" Then
Target.Offset(, -4).Resize(, 5).Interior.Color = vbYellow
End If
End Sub
Try:
Option Explicit
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim cell As Range
If Not Intersect(Target, Sh.UsedRange) Is Nothing Then
For Each cell In Target.Cells
With cell
If UCase(.Value) = "RISK" Then
.Font.Color = vbRed
Else
.Font.Color = vbBlack
End If
End With
Next cell
End If
End Sub

Accumulating values that are input into single Excel cell

I'm trying to create a cell in Excel that resets every time I put a value into it and every value that I put in the cell is stored and added together.
So basically cell A1 would be empty and then I add a value 30, for example. The cell would then store that value and reset to receive more inputs. I then go ahead and put another value in cell A1, 20. The cell should once again reset, but the value stored in cell A1 would now be equal to 50.
I'm very new to VBA so I'm still trying to figure everything out. I tried using some code I found in another post, but was not able to make it work so I was wondering if anyone had any idea on how to proceed with this problem.
This is the code I found and wasn't able to make it work. It was supposed to receive a value in cell A1 and store the same in cell A2, and once you add a new value to A1, it adds it to the previous value in A2.
Private Sub Worksheet_Change(ByVal Target As Range)
If Cells(1, 1).Value <> gdDouble Then
gdDouble = Cells(1, 1).Value
Cells(2, 1).Value = Cells(2, 1).Value + Cells(1, 1).Value
End If
End Sub
Private Sub Workbook_Open()
gdDouble = Sheets("sheet1").Cells(1, 1).Value
End Sub
And in the standard module:
dim gdDouble as double
Thank you
Adjust the code in the worksheet_change event like that
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Range("A1")) Is Nothing Then Exit Sub
'If Target.CountLarge > 1 Then Exit Sub
On Error GoTo EH
Application.EnableEvents = False
Target.Value = Target.Value + gdDouble
gdDouble = Target.Value
EH:
Application.EnableEvents = True
End Sub
And change gdDouble to a public variable
Public gdDouble As Double

Worksheet_Change Event - Duplication Check, Ignore Blanks

I am using a VBA change event to look for duplicates in column C. The code below works but when i delete all values within the range, blanks are triggered as duplicates so i need to include a way to ignore duplicates from the code. Any ideas?
Private Sub Worksheet_Change(ByVal Target As Range)
Dim cell As Range
On Error GoTo ws_exit
Application.EnableEvents = False
With Target
If .Column = 3 Then
With .EntireColumn
Set cell = .Find(What:=Target.Value, AFter:=.Cells(1, 1))
If cell.Address = Target.Address Then
Set cell = .FindNext()
End If
If Not cell.Address = Target.Address Then
MsgBox "This Wall Reference already exists. Please ensure you have a unique reference identifier less than 20 characters in length", vbOKOnly
End If
End With
End If
End With
ws_exit:
Application.EnableEvents = True
End Sub
I expect to be able to ignore blanks but sill have the VBA run a duplication check to return a msgbox only if a duplication is found.
First you must consider that Target is a range of multiple cells and not only one cell. Therefore it is necessary to use Intersect to get all the cell that are changed in column 3 and then you need to loop through these cells to check each of them.
Also I recommend to use WorksheetFunction.CountIf to count how often this value occurs if it is >1 then it is a duplicate. This should be faster then using Find.
Note that the following code looks for duplicates in column 3 only if you want to check if a duplicate exists anywhere in the worksheet replace CountIf(Me.Columns(3), Cell.Value) with CountIf(Me.Cells, Cell.Value)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim AffectedRange As Range
Set AffectedRange = Intersect(Target, Me.Columns(3))
If Not AffectedRange Is Nothing Then
Dim Cell As Range
For Each Cell In AffectedRange
If Application.WorksheetFunction.CountIf(Me.Columns(3), Cell.Value) > 1 Then
MsgBox "This Wall Reference already exists. Please ensure you have a unique reference identifier less than 20 characters in length", vbOKOnly + vbExclamation
End If
Next Cell
End If
End Sub
Instead of using VBA you could also use Conditional Formatting to highlight duplicates in red for example. Could be easier to archieve (use the =CountIf formula as condition). And also it will always highlight all duplicates immediately which makes it easy to determine them.
Thanks for the help K.Davis. I appreciate your time and effort.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Value = vbNullString Then Exit Sub
Dim cell As Range
On Error GoTo ws_exit
Application.EnableEvents = False
With Target
If .Column = 3 Then
With .EntireColumn
Set cell = .Find(What:=Target.Value, AFter:=.Cells(1, 1))
If cell.Address = Target.Address Then
Set cell = .FindNext()
End If
If Not cell.Address = Target.Address Then
MsgBox "This Glazing Reference already exists. Please ensure you have a unique reference identifier less than 20 characters in length", vbOKOnly
End If
End With
End If
End With
ws_exit:
Application.EnableEvents = True
End Sub

Change Formatting if cell is in a range

What I'm trying to do is when a cell (A1) matches something in a named range ("Names") then it changes colour, however if it doesn't but matches something a different named range ("Eye") then it becomes a different colour (there are many more ranges, but I'm sure I'll be able to figure it out after I have two working)
Things to note:
I know this can be done with conditional formatting, however due to the number of named ranges, and sizes of the ranges I was hoping it would be easier using a macro.
I so far have managed to get it working for one named range, and when A1 isn't a formula (however A1 will be)
My 2 lots of code so far are (note this is under sheet1):
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$1" Then
Application.Run ("Colour")
End If
End Sub
The my second one (is being a seperate module):
Sub Colour()
With ActiveSheet
For Each c In .Range("Names").Cells
If c.Value = .Range("A1").Value Then
Range("A1").Select
With Selection.Interior
.Color = 5287936
End With
End If
Next c
End With
End Sub
I think this does what you want:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("A1")) Is Nothing Then
ApplyColor Me.Range("A1")
End If
End Sub
Sub ApplyColor(ValueRange As Range)
Dim MatchRanges As Variant
Dim MatchColors As Variant
Dim MatchValue As Variant
Dim i As Long
MatchRanges = Array("Names", "Eye")
MatchColors = Array(5287936, 4287952)
MatchValue = ValueRange.Value
ValueRange.Interior.Color = vbWhite
For i = LBound(MatchRanges) To UBound(MatchRanges)
If WorksheetFunction.CountIf(Me.Range(MatchRanges(i)), MatchValue) > 0 Then
ValueRange.Interior.Color = MatchColors(i)
Exit For
End If
Next i
End Sub
A couple of notes: "Color" is a VBA reserved word and could cause issues, so I used something else for your sub name. You don't need to use Application.Run in this situation, just the sub's name and its arguments (or Call if you prefer).

Resources