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).
Related
I have the following code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$N$16" Then
Sheet3.Unprotect ""
Call QuantityisActivated(Target)
Sheet3.Protect ""
End If
End Sub
How can i use the same code for multiple Target.Adress for example, I want here from N16 to N30 range of cells
Sub QuantityisActivated(Target)
MsgBox "This is a sample box"
End Sub
The following can be used to only fire the QuantityisActivated if cells within the range are changed:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, c As Range
Set rng = Intersect(Target, Range("N16:N30"))
If Not rng Is Nothing Then
'If you want it to run on each cell withing the range..
For Each c In rng.Cells
Call QuantityisActivated(c)
Next
'Or if you want it to run once, if any cell in range is affected
Call QuantityisActivated(rng)
End If
End Sub
I've included two possible ways of dealing with the affected range - either at a cell by cell level, or as an entire range as it's not clear which you'd prefer from your question.
I'm new to VBA so I'm probably making some beginner mistakes, please bare with me.
Here is the summary of my goal : I have several sheets in an Excel Workbook with the same structure. In each of those, I have a "Project Status" column with numbers ranging from 0 to 12. I'm trying to monitor a change in the column and, if the value of a cell changes, the row gets moved to the corresponding sheet and location.
My problem is that my code works but leaves an empty row where the row was cut. I tried adding
Target.EntireRow.Delete
but, if I add it before Insert the inserted row is empty, if I add it after it doesn't seem to do anything.
Here is a shorter version of my code, that I have in every sheet that is concerned by it :
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = Range("A:A")
If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
On Error GoTo bm_Safe_Exit
Application.ScreenUpdating = False
If Target.Value = 0 Then
Target.EntireRow.Cut
IdeasUpcoming.Range("4:4").Insert
End If
If Target.Value = 1 Then
Target.EntireRow.Cut
IdeasUpcoming.Range("4:4").Insert
End If
If Target.Value = 2 Then
Target.EntireRow.Cut
Current.Range("STATUSNewProjects").Offset(1, 0).Insert
End If
If Target.Value = 3 Then
Target.EntireRow.Cut
Current.Range("STATUSAdvancedProjects").Offset(1, 0).Insert
End If
If Target.Value = 4 Then
Target.EntireRow.Cut
Completed.Range("STATUSFinished").Offset(1, 0).Insert
End If
If Target.Value = 5 Then
Target.EntireRow.Cut
Completed.Range("STATUSOld").Offset(1, 0).Insert
End If
End If
bm_Safe_Exit:
Application.ScreenUpdating = True
End Sub
How can I delete the row I'm cutting? I'm sure the If / End If for each cell value aren't optimal, is there a way to simplify this (considering this is shortened, in reality I have 13 values)?
Thank you a lot for your help.
You can use the range.copy logic like this - then you can delete the row afterwards:
With Target.EntireRow
.Copy IdeasUpcoming.cells(4,1)
.Delete xlShiftUp
End With
Regarding your multiple checks:
Maybe you can create a configuration array, which holds per index the target sheets range after that the row should be inserted
Dim arrTarget(1 to 15) as range
set arrTarget(1) = IdeasUpcoming.Cells(4,1)
...
set arrTarget(4) = Completed.Range("STATUSFinished")
Then you can use it like this - without Ifs:
'insert new row for row to be copied
arrTarget(Target.value).Offset(1).EntireRow.Insert xlShiftDown
With Target.EntireRow
.Copy arrTarget(Target.value).Offset(1)
.Delete xlShiftUp
End With
Furthermore you should have one generic copy routine in a normal module
Public sub moveRows(Target as range)
'define arrTarget
'do the copying
End sub
And then you call this generic routine from either all worksheet_change routines
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = Range("A:A")
If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
moveRows target '-- this is where you call the generic sub
end if
End Sub
Or - if you have a sheetname logic to identify the relevant worksheets, e.g. data1, data2 etc. then you could use the workbook_SheetChange event (in the ThisWorkbook-module)
```vba
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Sh.Name Like "data*" Then
Dim KeyCells As Range
Set KeyCells = Range("A:A")
If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
moveRows Target '-- this is where you call the generic sub
End If
End If
End Sub
In case you have to make changes to your move-routine or the worksheet_change event, you only have to make changes in one place :-). (DRY: Don't repeat yourself)
I am newbie at excel and VBA so I came here to ask for your help.
I am looking for a VBA code to automatically color the first letter of each column cell.This is expected for column A only, not all columns in excel.
There are only words in the column.
For example, if the first letter is 'a' then the 'a' only will become red.There is no limit of case-sensitive and each of a-z will be colored in 26 distinguishable colors. I tried for a couple of days to find a solution, but unfortunately I couldn't.
Demonstration:
Thanks in advance.
There’s (at least) a couple of ways to do this. The first option below selects the color for you – you don’t get a choice, but the code is much shorter. The second option will require you to hard code the actual color you want for each letter – I’ve only gone as far as C for the sake of demonstration.
In both cases, you paste the code into the Sheet module area for the appropriate sheet. Let me know how it goes for you. I've taken this approach because you said you wanted it to occur "automatically"
Option One – shorter but no choice of color
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo GetOut
Application.EnableEvents = False
Dim myFont As Integer, aCell As Range
For Each aCell In Target.Cells
myFont = Asc(UCase(Left(aCell, 1))) - 62
aCell.Characters(Start:=1, Length:=1).Font.ColorIndex = myFont
Next
Continue:
Application.EnableEvents = True
Exit Sub
GetOut:
MsgBox Err.Description
Resume Continue
End Sub
Option Two – you choose the color you want, but must be added
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo GetOut
Application.EnableEvents = False
Dim myLetter As String, aCell As Range
For Each aCell In Target.Cells
myLetter = UCase(Left(aCell, 1))
Select Case myLetter
Case Is = "A"
aCell.Characters(Start:=1, Length:=1).Font.ColorIndex = 3 `<~~ change to your taste
Case Is = "B"
aCell.Characters(Start:=1, Length:=1).Font.ColorIndex = 4
Case Is = "C"
aCell.Characters(Start:=1, Length:=1).Font.ColorIndex = 5
'*** etc etc etc Add the rest of the alphabet***
End Select
Next
Continue:
Application.EnableEvents = True
Exit Sub
GetOut:
MsgBox Err.Description
Resume Continue
End Sub
To apply this to any (or certian) worksheets in the Workbook containing the code, place this code in the ThisWorkbook module
Option Explicit
Private Colours As Variant
' Define Colour Pallete
Private Sub PopulateColours()
ReDim Colours(0 To 25)
Colours(0) = vbRed 'A
Colours(1) = vbBlue 'B
' etc C..Z
End Sub
' Colour first character of each non-formula cell in range
Private Sub ColourCells(rng As Range)
Dim cl As Range
' if pallet not set, initialise it
If IsEmpty(Colours) Then PopulateColours
' loop the range
For Each cl In rng
' ignore formula, numeric and empty cells
If Not IsEmpty(cl) Then
If Not cl.HasFormula Then
If Not IsNumeric(cl.Value2) Then
If Not cl.Value2 = "" Then
With cl.Characters(1, 1)
.Font.Color = Colours(Asc(UCase(.Text)) - 65)
End With
End If
End If
End If
End If
Next
End Sub
' when and cell on any worksheet in the workbook changes...
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
' select specific sheets to apply colour to
Select Case Sh.Name
Case "Sheet1", "Sheet2"
' only colour column A
If Not Application.Intersect(Target, Sh.Columns(1)) Is Nothing Then
' call colouring routine
ColourCells Target.Columns(1)
End If
End Select
End Sub
If you want to apply this to any (or certain) open workbooks, you'll need an Application Events handler
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
I want to activate a macro on a change in a range.
The following code works fine except I want a variable last row (Where B100 currently is).
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B1:B100")) Is Nothing Then
MsgBox "Updating sheets"
Call Thickborders2
End If
End Sub
The B100 in the range is dependent on the last row with text in it.
You can borrow a worksheet trick to find the last row with text in it.
=MATCH("zzz", B:B)
The above returns the last row in column B with a text value.
Private Sub Worksheet_Change(ByVal Target As Range)
dim m as variant
m = application.match("zzz", columns("B"))
if iserror(m) then m = 1
If Not Intersect(Target, Range("B1").resize(m, 1)) Is Nothing Then
MsgBox "Updating sheets"
Call Thickborders2
End If
End Sub
I strongly recommend adding error control (on error goto <label>) and disabling event triggers (application.enableevents = false). Remember to re-enable events before exiting the sub.
As I said in my comment on the OP - Worksheet_Change on its own won't work as it will calculate the last cell based on the data just entered.
This code calculates the last cell when you move cells (I tried on the Calculate event but that happens after you've added the data so same problem as the Change event).
Option Explicit
Private rLastCell As Range
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Set rLastCell = Cells(Rows.Count, 2).End(xlUp)
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range(Cells(1, 2), rLastCell)) Is Nothing Then
MsgBox "Updating sheets"
Call Thickborders2
End If
End Sub
The first two lines must be at the very top of the module.
Building on comments from Taazar and L42 try:
Private Sub Worksheet_Change(ByVal Target As Range)
LastCell = Activesheet.Usedrange.Rows.Count
If Not Intersect(Target, Range("B1:B" & LastCell)) Is Nothing Then
MsgBox "Updating sheets"
Call Thickborders2
End If
End Sub
Where Activesheet should be replaced by the sheetname you're checking.