VBA - Conditional Formatting Multiple Ranges of Cells and Looping - vba

I am creating a Master Schedule for work and am trying to display information so it is very intuitive to the user.
So... we would like to be able to Highlight Cells J4:L4 when a date is entered in Cell K4. Then, we want to loop it for Rows 4 through 2500 and Columns M:0, P:R, S:U, etc.... I have the following Macro written but it is only highlighting the first Row. Can someone point me in the right direction?
Sub Highlight()
'
' Highlight Macro
'
' Keyboard Shortcut: Ctrl+Shift+R
'
Dim kRange As Range, k As Integer, aaaFormat As FormatCondition
If ActiveSheet.Name <> Sheet1.Name Then Exit Sub
For k = 4 To 2500
Set kRange = Range("=$J4:$L4")
If kRange.FormatConditions.Count <> 0 Then
kRange.FormatConditions.Delete
Else
Set aaaFormat = kRange.FormatConditions.Add(xlExpression, xlFormula, "=$K4<>0")
aaaFormat.Interior.Color = 15773696
End If
Next k
End Sub

While you are cycling through the loop, you need to adjust the range the CF rule will govern as well as the formula that determines its outcome.
'this,
Set kRange = Range("$J4:$L4")
'becomes,
Set kRange = Range("$J"& k & ":$L"& k)
'and this,
Set aaaFormat = kRange.FormatConditions.Add(xlExpression, xlFormula, "=$K4<>0")
'becomes,
Set aaaFormat = kRange.FormatConditions.Add(xlExpression, xlFormula, "=$K" & k & "<>0")
However, since you've already made the row relative and the column absolute, you can simply write the CF to the entire range.
Sub Highlight()
' Highlight Macro
' Keyboard Shortcut: Ctrl+Shift+R
With Sheet1.Range("J4:L2500")
.FormatConditions.Delete
With .FormatConditions.Add(Type:=xlExpression, Formula1:="=$K4<>0")
.Interior.Color = 15773696
End With
End With
End Sub

Related

Change Highlight Color After User input in Excel vba

I have a piece of code that will allow me to find the next free cell in column F, where an operator will input a Weight.
I wanted to help by highlighting the cells in a range close to where the input needs to be done (this can help to check that the entry is correct without filtering).
I can do that with the code below but I'm trying to remove the highlighting after the cell is written and I'm failing. I tried an approach with 'Do Until' but was also not satisfactory. The code runs but it does not remove the highlight once the user adds a value.
I have also tried using Wait functions but they freeze Excel completely (I cannot modify any value). Additionally, when I run in debug and use a random iteration to modify the Cell value, my code works.
'Find the last non-blank cell in column F (aka, column 6)
' We will add i rows to make the ith blank (in the for loop)
PreFree = Cells(Rows.Count, 6).End(xlUp).Row
NextFree = PreFree + 1
' Select Cell for manual input
Range("F" & NextFree).Select
'Do Until emptyWeight = False
If ThisWorkbook.Sheets("Input").Range("F" & NextFree) = "" Then
emptyWeight = True
Range(Cells(NextFree, "C"), Cells(NextFree, "F")).Interior.Color = RGB(181, 244, 0)
Else
Range(Cells(NextFree, "C"), Cells(NextFree, "F")).Interior.Color = RGB(255, 255, 255)
emptyWeight = False
End If
As Sir BruceWayne said, you can do this with Worksheet_Change event.
Private Sub Worksheet_Change(ByVal Target As Range)
' Declare and prepare the intersection & union of ranges
Dim rIntersect As Range
Set rIntersect = Intersect(Target.Offset(1, 0), Sheets("YourSheet").Range("F:F"))
' Exit this event if there is no change in column f
If rIntersect Is Nothing Then Exit Sub
' Include TargetCell
Set rIntersect = Union(Target, rIntersect)
' Declare the 'each range', in case the user will paste values into column f
Dim rEach As Range
' Will loop through each cell that made a change in column f
For Each rEach In rIntersect
' Give default color.
rEach.Interior.Color = RGB(255, 255, 255)
' And test if the value <blank>, sets color if it is true.
If rEach.Value = "" Then rEach.Interior.Color = RGB(181, 244, 0)
Next
End Sub
Hope this works for you. Good luck!
To OP, paste your code here

Using a VBA copy sheet macro and formula r1c1

Maybe I've been staring at this for too long, but I have a macro that copies worksheets in Excel that works. What I'm also trying to do is include this into the loop (just the R1C1 formula from this recorded macro):
Sub Macro4()
'
' Macro4 Macro
'
' Keyboard Shortcut: Ctrl+a
'
Sheets("<Null>").Select
ActiveSheet.Buttons.Add(541.5, 97.5, 95.25, 43.5).Select
ActiveSheet.Buttons.Add(541.5, 169.5, 94.5, 42.75).Select
Sheets("<Null>").Copy After:=Sheets(3)
ActiveCell.FormulaR1C1 = "='Dividing Walls Only'!RC[-2]"
Range("C4").Select
Sheets("<Null> (2)").Select
ActiveSheet.Buttons.Add(541.5, 97.5, 95.25, 43.5).Select
ActiveSheet.Buttons.Add(541.5, 169.5, 95.25, 42.75).Select
Sheets("<Null> (2)").Copy After:=Sheets(4)
Range("C3").Select
ActiveCell.FormulaR1C1 = "='Dividing Walls Only'!R[1]C[-2]"
Range("C4").Select
Sheets("<Null> (3)").Select
ActiveSheet.Buttons.Add(541.5, 97.5, 95.25, 43.5).Select
ActiveSheet.Buttons.Add(541.5, 169.5, 95.25, 42.75).Select
Sheets("<Null> (3)").Copy After:=Sheets(5)
Range("C3").Select
ActiveCell.FormulaR1C1 = "='Dividing Walls Only'!R[2]C[-2]"
Range("C4").Select
End Sub
Obviously, this would be silly to repeat 180 times. This is the Copy Sheet macro that I have already:
Sub CopySheet()
Call OptimizeCode_Begin
Dim x As Integer
x = InputBox("Enter number of times to copy active sheet")
For numtimes = 1 To x
'Loop by using x as the index number to make x number copies
ActiveWorkbook.ActiveSheet.Copy _
After:=ActiveWorkbook.Sheets(3)
'Put copies in front of Sheet3
'Might need to move the new sheets
Next
Call OptimizeCode_End
End Sub
What I would like to do is either incorporate a nested loop or something to automatically advance the R1C1 formula for each sheet that would keep me from having to type in the cell I'm trying to reference after all the sheets have copied. Any help would be appreciated.
Thanks!
Justin
From what I could understand from your post, the code below will run according to the number of times selected by the user in InputBox, and copy a Sheet at after the last on.
For each created Sheet it will add a Formula to Cell C4, I'm just not sure the logics in advancing the Formula for each sheet.
Sub CopySheets()
Dim x As Long
Dim numtimes As Long
Dim newSht As Worksheet
x = Application.InputBox("Enter number of times to copy active sheet", Default:=1, Type:=1)
' optimize run time
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
Application.DisplayAlerts = False
' create the Buttons on the original sheet
' (will be copied inside the loop for all other sheets)
ActiveSheet.Buttons.Add(541.5, 97.5, 95.25, 43.5).Select
ActiveSheet.Buttons.Add(541.5, 169.5, 94.5, 42.75).Select
For numtimes = 1 To x
'Loop by using x as the index number to make x number copies
ActiveWorkbook.ActiveSheet.Copy _
After:=ActiveWorkbook.Sheets(ActiveWorkbook.Sheets.Count)
Set newSht = ActiveWorkbook.Sheets(ActiveWorkbook.Sheets.Count)
' give the new Sheet Name the reference of num of times
newSht.Name = "<NULL " & numtimes & ">"
' advance the row number in the formula
newSht.Range("C3").FormulaR1C1 = "='Dividing Walls Only'!R[" & numtimes & "1]C[-2]"
Next numtimes
' Resume Settings
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
Application.DisplayAlerts = True
End Sub
may be this is what you are after:
Option Explicit
Sub CopySheet()
Dim numtimes As Long, x As Long, rowIndex As Long
Call OptimizeCode_Begin
rowIndex = 4 '<-- this is the row index that will be used in the formula that'll be written in the first new sheet
numtimes = Application.InputBox("Enter number of times to copy active sheet", Default:=1, Type:=1)
For x = 1 To numtimes
'Loop by using x as the index number to make x number copies
ActiveWorkbook.ActiveSheet.Copy _
After:=ActiveWorkbook.Sheets(3)
Range("C3").Formula = "='Dividing Walls Only'!A" & rowIndex '<--| write formula in the new sheet cell "C3" referencing "Dividing Walls Only" worksheet column "A" cell in current 'rowIndex'
rowIndex = rowIndex + 1 '<--| update row index for subsequent new sheet formula
Next
Call OptimizeCode_End
End Sub
you see I used Excel (i.e. Application) InputBox() method instead of VBA InputBox() one since the former lets you specify the return data type also (Type:=1 for numeric input), thus forcing the user input to the wanted one.

Excel VBA delete entire row if cell in column D is empty

Can anyone walk me through how to write a script to delete the entire row if a cell in column D = "" on sheet 3 in range D13:D40.
Also, how to prevent the user from accidentally running the script again once those cells in the range are already deleted and other cells are now on the D13:D40 range?
Solution: This is working for me:
Sub DeleteRowsWithEmptyColumnDCell()
Dim rng As Range
Dim i As Long
Set rng = ThisWorkbook.ActiveSheet.Range("D13:D40")
With rng
' Loop through all cells of the range
' Loop backwards, hence the "Step -1"
For i = .Rows.Count To 1 Step -1
If .Item(i) = "" Then
' Since cell is empty, delete the whole row
.Item(i).EntireRow.Delete
End If
Next i
End With
End Sub
Explanation: Run a for loop through all cells in your Range in column D and delete the entire row if the cell value is empty. Important: When looping through rows and deleting some of them based on their content, you need to loop backwards, not forward. If you go forward and you delete a row, all subsequent rows get a different row number (-1). And if you have two empty cells next to each other, only the row of the first one will be deleted because the second one is moved one row up but the loop will continue at the next line.
No need for loops:
Sub SO()
Static alreadyRan As Integer
restart:
If Not CBool(alreadyRan) Then
With Sheets("Sheet3")
With .Range("D13:D40")
.AutoFilter 1, "="
With .SpecialCells(xlCellTypeVisible)
If .Areas.Count > 1 Then
.EntireRow.Delete
alreadyRan = alreadyRan + 1
End If
End With
End With
.AutoFilterMode = False
End With
Else
If MsgBox("procedure has already been run, do you wish to continue anyway?", vbYesNo) = vbYes Then
alreadyRan = 0
GoTo restart:
End If
End If
End Sub
Use AutoFilter to find blank cells, and then use SpecialCells to remove the results. Uses a Static variable to keep track of when the procedure has been run.
Here's my take on it. See the comments in the code for what happens along the way.
Sub deleterow()
' First declare the variables you are going to use in the sub
Dim i As Long, safety_net As Long
' Loop through the row-numbers you want to change.
For i = 13 To 40 Step 1
' While the value in the cell we are currently examining = "", we delete the row we are on
' To avoid an infinite loop, we add a "safety-net", to ensure that we never loop more than 100 times
While Worksheets("Sheet3").Range("D" & CStr(i)).Value = "" And safety_net < 100
' Delete the row of the current cell we are examining
Worksheets("Sheet3").Range("D" & CStr(i)).EntireRow.Delete
' Increase the loop-counter
safety_net = safety_net + 1
Wend
' Reset the loop-counter
safety_net = 0
' Move back to the top of the loop, incrementing i by the value specified in step. Default value is 1.
Next i
End Sub
To prevent a user from running the code by accident, I'd probably just add Option Private Module at the top of the module, and password-protect the VBA-project, but then again it's not that easy to run it by accident in the first place.
This code executes via a button on the sheet that, once run, removes the button from the worksheet so it cannot be run again.
Sub DeleteBlanks()
Dim rw As Integer, buttonID As String
buttonID = Application.Caller
For rw = 40 To 13 Step -1
If Range("D" & rw) = "" Then
Range("D" & rw).EntireRow.Delete
End If
Next rw
ActiveSheet.Buttons(buttonID).Delete
End Sub
You'll need to add a button to your spreadsheet and assign the macro to it.
There is no need for loops or filters to find the blank cells in the specified Range. The Range.SpecialCells property can be used to find any blank cells in the Range coupled with the Range.EntireRow property to delete these. To preserve the run state, the code adds a Comment to the first cell in the range. This will preserve the run state even if the Workbook is closed (assuming that it has been saved).
Sub DeleteEmpty()
Dim ws As Excel.Worksheet
Set ws = ActiveSheet ' change this as is appropriate
Dim sourceRange As Excel.Range
Set sourceRange = ws.Range("d13:d40")
Dim cmnt As Excel.Comment
Set cmnt = sourceRange.Cells(1, 1).Comment
If Not cmnt Is Nothing Then
If cmnt.Text = "Deleted" Then
If MsgBox("Do you wish to continue with delete?", vbYesNo, "Already deleted!") = vbNo Then
Exit Sub
End If
End If
End If
Dim deletedThese As Excel.Range
On Error Resume Next
' the next line will throw an error if no blanks cells found
' hence the 'Resume Next'
Set deletedThese = sourceRange.SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
If Not deletedThese Is Nothing Then
deletedThese.EntireRow.Delete
End If
' for preserving run state
If cmnt Is Nothing Then Set cmnt = sourceRange.Cells(1, 1).AddComment
cmnt.Text "Deleted"
cmnt.Visible = False
End Sub
I've recently had to write something similar to this. I'm not sure that the code below is terribly professional, as it involves storing a value in cell J1 (obviously this can be changed), but it will do the job you require. I hope this helps:
Sub ColD()
Dim irow As long
Dim strCol As String
Sheets("sheet2").Activate
If Cells(1, 10) = "" Then
lrun = " Yesterday."
Else: lrun = Cells(1, 10)
End If
MsgBox "This script was last run: " & lrun & " Are you sure you wish to continue?", vbYesNo
If vbYes Then
For irow = 40 To 13 step -1
strCol = Cells(irow, 4).Value
If strCol = "" Then
Cells(irow, 4).EntireRow.Delete
End If
Next
lrun = Now()
Cells(1, 10) = lrun
Else: Exit Sub
End If
End Sub

Increase speed of changing Excell cell's value with just a mouse [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I would like to increase the speed of changing Excel cell's value with a mouse only. I share my tool in hope that someone will like it and want to improve it.
This is an example. After clicking on a defined cell containing value, scrollbar appears on the right side of a cell. You can smoothly change its value with a mouse.
The tool is meant to change cells value and observe formulas values dynamically. You may simplify the code however some features should not be disabled. It should always stay dynamic, that is moving the srollbar should immediately influence other cells with formulas. The srollbar should not twinkle (changing colour grey and black).
You may simply download the scrollbar.xlsm file here and view the VBA code inside it.
Or you may put this code in your sheet where you want the scollbars to appear. The name of your sheet does not matter. Right click on the sheet's name and then click View Code. This is the place:
Insert there this code:
Option Explicit
Dim previousRow, c
Const scrlName As String = "scrlSh" ' the name of the scrollbar
Private Sub scrlSh_GotFocus()
ActiveSheet.Range(ActiveSheet.OLEObjects(scrlName).TopLeftCell.Offset(0, -1).Address).Activate
End Sub
Private Sub scrlSh_Scroll()
Dim rngCell As Range
Set rngCell = Sheets("Param").Range(ActiveSheet.OLEObjects(scrlName).LinkedCell)
ActiveSheet.OLEObjects(scrlName).TopLeftCell.Offset(0, -1).Value = _
rngCell.Offset(0, 1).Value + (ActiveSheet.OLEObjects(scrlName).Object.Value * rngCell.Offset(0, 3).Value)
Set rngCell = Nothing
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' Macro concept by Przemyslaw Remin, VBA code written by Jaroslaw Smolinski
' The Sub Worksheet_SelectionChange and function SearchAdr have to be on each sheet where scrollbars are to appear
' Sheet Param is one for all sheets, only the columns A-G are used, othre columns can be used for something else
' Do not change the layout of A-G columns unless you want to modify the code
' Addresses in Param have to be with dollars (i.e. $A$3) or it may be named ranges of single cells
' (if it starts with $ it is a cell, otherwise it is a named range)
' the lower or upper case in addresses does not matter
Dim SheetFly As String, adr As String
Dim cCell As Range
Dim actSheet As Worksheet
Dim shScroll As Object
Set actSheet = ActiveSheet
' checks if scrollbar exists
If actSheet.Shapes.Count > 0 Then
For Each shScroll In actSheet.Shapes
If shScroll.Type = msoOLEControlObject And shScroll.Name = scrlName Then
Exit For ' scrollbar found, and the variable is set
End If
Next shScroll
End If
' if scrollbar does not exists then it is created
If shScroll Is Nothing Then
Set shScroll = ActiveSheet.OLEObjects.Add(ClassType:="Forms.ScrollBar.1", Link:=False, _
DisplayAsIcon:=False, Left:=0, Top:=0, Width:=64 * 3, Height:=15)
' scrollbar length is set as three adjesent columns
shScroll.Visible = False
shScroll.Name = scrlName
shScroll.Placement = xlMoveAndSize
End If
shScroll.Visible = False
adr = Target.AddressLocal
SheetFly = actSheet.Name
' here we set up in which cells the scrollbar has to appear. We set up only the number of rows
Set cCell = SearchAdr(SheetFly, adr, Sheets("Param").Range("B2:B40")) ' If needed it can be longer i.e. B2:B400
If Not cCell Is Nothing Then
With ActiveSheet.OLEObjects(scrlName)
.LinkedCell = "" ' temporary turn off of the link to the cell to avoid stange behaviour
.Object.Min = 0 ' the scale begins from 0, not negative
.Object.Max = Abs((cCell.Offset(0, 4).Value - cCell.Offset(0, 3).Value) / cCell.Offset(0, 5).Value)
.Object.SmallChange = 10 ' single change by one step
.Object.LargeChange = 10 ' change by jumps after clicking on scrollbar bar ("page up", "page down")
If Target.Value <> cCell.Offset(0, 2).Value And Target.Value >= cCell.Offset(0, 3).Value And Target.Value <= cCell.Offset(0, 4).Value Then
' setting up the cells value as close as possible to the value of input by hand
' rounded by step
' if value is out of defined range then the last value will be used
cCell.Offset(0, 2).Value = Abs((Target.Value - cCell.Offset(0, 3).Value) / cCell.Offset(0, 5).Value)
End If
'Protection in case the value is out of min and max range
If cCell.Offset(0, 2).Value > .Object.Max Then
cCell.Offset(0, 2).Value = .Object.Max
ElseIf cCell.Offset(0, 2).Value < .Object.Min Then
cCell.Offset(0, 2).Value = .Object.Min
End If
Target.Value = cCell.Offset(0, 3).Value + (cCell.Offset(0, 5).Value * cCell.Offset(0, 2).Value)
.Object.Value = cCell.Offset(0, 2).Value
.LinkedCell = "Param!" & cCell.Offset(0, 2).Address 'setting up linked cell
End With
' Setting up the position and width of scrollbar with reference to the cell
shScroll.Top = Target.Top
shScroll.Left = Target.Offset(0, 1).Left + 2 'position to the right + small margin
shScroll.Width = Target.Offset(0, 5).Left - Target.Offset(0, 1).Left - 2 'width of 5 columns
shScroll.Visible = True
End If
Set actSheet = Nothing
Set shScroll = Nothing
Set cCell = Nothing
End Sub
Private Function SearchAdr(SheetFly As String, SearchCell As String, rng As Range) As Range
Dim cCell As Range
Dim oOOo As Name
' Searching for the row with parameter for chosen cell
' The parameter have to be in one, continouse range
For Each cCell In rng
If cCell.Text = "" Then ' check if parameters have not finished
Set SearchAdr = Nothing
Exit Function ' stop if you find first empty cell for speeding
ElseIf Left(cCell.Text, 1) = "$" Then ' normal address
If cCell.Offset(0, 1).Text & "!" & UCase(cCell.Text) = SheetFly & "!" & UCase(SearchCell) Then
Set SearchAdr = cCell
Exit Function ' exit if find proper row with parameters
End If
Else ' means that found is a name
For Each oOOo In ActiveWorkbook.Names
If (oOOo.RefersTo = "=" & SheetFly & "!" & UCase(SearchCell)) And (UCase(oOOo.Name) = UCase(cCell.Text)) Then
Set SearchAdr = cCell
Exit Function ' exit if find proper row with parameters
End If
Next oOOo
End If
Next cCell
End Function
In your workbook you have to make sheet named Param where the parameters of scrollbar are stored. In column A and C put the name of your sheet where you want scrollbars to appear. The sheet looks like this:
Now you can enjoy the scrollbar after clicking the cell in the model sheet.
Note that you can define different min, max ranges and step of scrollbar change separately for every cell. Moreover, the min and max range can be negative.
I'd prefer:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Intersect(Target, Range("A1:A10")) Is Nothing Then Exit Sub
If OLEObjects.Count = 0 Then OLEObjects.Add "Forms.ScrollBar.1", , , , , , , Target.Offset(, 1).Left, Target.Top, 199, 15
With OLEObjects(1)
.Top = Target.Top
.object.max=200
Target = Application.Max(Target, .Object.Min)
Target = Application.Min(Target, .Object.Max)
.LinkedCell = Target.Address
End With
End Sub
To make the value change when clicking on left/right arrow or inside the scrollbar, I'd rather add:
Private Sub scrlSh_Change()
If ActiveSheet.OLEObjects(scrlName).LinkedCell <> "" Then
scrlSh_Scroll
End If
End Sub
I'd prefer use typed function like UCase$, Left$, ... rather than their variant equivalent (UCase, Left, ...), but for this macro the "true" performance is not really required.
Within your Worksheet_SelectionChange sub, I've replaced the actSheet, SheetFly and adr variables by their original values (as there are used only once). No real big improvment yet.

Loop through all cells and delete any zero cell (shift up)

This seems like it should be a fairly simple task but I can't seem to get it to work- I just want a simple macro that will loop through all the cells in my worksheet. If the cell is equal to zero (or blank for kicks), then just delete it, moving the other cells in the column up one.
I got nowhere trying to do it on the whole document, so I tried to do it on a single column :
Sub Macro6()
'
' Macro6 Macro
'
' Keyboard Shortcut: Ctrl+q
'
For i = 81 To 1 Step -1
If Range("A" & i) = "0" Then Range("A" & i).Delete
Next i
End Sub
I don't want to delete any rows, just the individual cells if they have value 0.
But evidently still getting nowhere.
Select the range to delete blanks within. Home > Editing – Find & Select, Go To Special, Blanks, OK, select within array, Delete…, Shift cells up, OK.
To apply the same process to cells containing 0 select range to delete 0s within and start (ie before above) by replacing these with nothing:
Home > Editing, Find & Select, enter 0 in Find what:, check Match entire cell contents and ensure Replace with: is empty, Replace All.
This small macro will delete both blanks and zeros:
Sub dural()
Dim rKill As Range, r As Range
Set rKill = Nothing
For Each r In ActiveSheet.UsedRange
If r.Value = 0 Or r.Value = "" Then
If rKill Is Nothing Then
Set rKill = r
Else
Set rKill = Union(rKill, r)
End If
End If
Next
If rKill Is Nothing Then
Else
rKill.Delete Shift:=xlUp
End If
End Sub
As pnuts pointed out, this can be a little slow. The following might be a little quicker:
Sub dural2()
With Cells
.Replace "0", ""
.SpecialCells(xlCellTypeBlanks).Delete Shift:=xlUp
End With
End Sub