Nested loop does not run properly - vba

I have a nested loop which first changes all ref cells to white font using a for loop and in a sub for loop the ref cells are deleted. The code runs but has still one or two cells with reference errors. Would need help to sort out the code so that it can remove all reference errors instead of most of it! Thank you .
Sub Delete_ref_basedontextcondition()
Dim R As Range
Dim w As Long, ref As Range
Dim refi As Range
On Error Resume Next
'Set rng = Nothing
On Error Resume Next
Set R = Application.InputBox("Select cells To be deleted", Type:=8)
Dim rng As Range
If TypeName(R) <> "Range" Then
Exit Sub
Else
R.Delete
End If
For w = 1 To Worksheets.Count
With Worksheets(w)
For Each ref In .Cells.SpecialCells(xlCellTypeFormulas, xlErrors)
If ref.Text = "#REF!" Then
ref.Font.ColorIndex = 2
For Each refi In .Cells.SpecialCells(xlCellTypeFormulas, xlErrors)
If refi.Text = "#REF!" Then
refi.Delete
End If
Next refi
End If
Next ref
End With
Next w
End Sub

I'm believe that the rogue #REF! is actually created by refi.Delete so it is missed by the loop that was defined prior to its creation. Use refi.Clear instead. You just want to remove the bad formula; you do not want to delete the cell and shift other cells around it (thereby creating new #REF! errors).
Think of it as a Pez dispenser full of #REF! errors. Every time you pop one out, another comes in to take its place. Close the lid with .Clear instead of spiralling through popping them out with .Delete.

Related

Worksheet_Change setting target range is slow

I have an excel macro used to manage button visibility in Excel in the "Worksheet_Change" function based from another SO question here.
The problem is the although the macro works it makes updating the Excel sheet rather laggy. I have managed to pin down the slowness to a single line:
Set rUpdated = Range(Target.Dependents.Address)
This sets the range of cells updated to a variable to be iterated through later in the script. If I call a script with just this line I found this is where all the delay is. It seems a rather simple line, but is there a better way to do it?
Full disclosure:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rUpdated As Range
Dim shp As Shape
Dim rCell As Range
Set rUpdated = Range(Target.Dependents.Address)
If Not rUpdated Is Nothing Then
For Each rCell In rUpdated
If rCell.Column = 1 Then
'Look at each shape in the sheet and cross-reference with rCell.
For Each shp In Target.Parent.Shapes
If shp.TopLeftCell.Row = rCell.Row Then
shp.Visible = (rCell.Value <> "")
Exit For 'Exit the loop - the correct button has been found.
End If
Next shp
End If
Next rCell
End If
End Sub
So if i understood it correctly you want to make a button visible if the cell in the row as been changed. The only things i can think of to slow it down are, that is has to check many rCell or Shapes. I dont know what the structure of your document is. So my Idea would be: instead of going through all shapes every time, i would name them in a pattern that you can identify them with the row they are in so you use the name to address them (i.e Row2 for the Button in Row 2).
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rUpdated As Range
Dim shp As Shape
Dim rCell As Range
Dim obj As OLEObject
Set rUpdated = Range(Target.Dependents.Address)
If Not rUpdated Is Nothing Then
For Each rCell In rUpdated
If rCell.Column = 1 Then
On Error Resume Next
Set obj = ActiveSheet.OLEObjects("Row" & rCell.Row)
If Err.Number = 0 Then
obj.Visible = (rCell.Value <> "")
End If
End If
Next rCell
End If
End Sub
I replaced that config with the following single line (and companion line):
On Error Resume Next
ActiveSheet.Shapes("buttonRow" & Target.Row).Visible = (ActiveSheet.Cells(Target.Row, 1).Value <> "")
However to get this to work I first needed to rename all my shapes. I used this function to do that:
Function renamebuttons()
For Each shp In ActiveSheet.Shapes
shp.name = "buttonRow" & shp.TopLeftCell.Row
Next shp
End Function
I ran that function once and deleted it. Once done my shapes can now be referred to by name and I no longer incur the delay of cycling through every shape and every target dependent. The delay experienced in the worksheet is now minimal.

Hiding Reference Errors using Font colour in VBA

Hi there i have this code which changes cells with reference errors to white fonts. However i could only do so for a single sheet. range. How do i change the for each loop to loop for all the worksheets in the workbook? I used this code below but it does not seem to work!
Sub Delete_ref_basedontextcondition()
Dim R As Range
'Set rng = Nothing
On Error Resume Next
Set R = Application.InputBox("Select cells To be deleted", Type:=8)
Dim rng As Range
If TypeName(R) <> "Range" Then
Exit Sub
Else
R.Delete
End If
For k = 1 To ThisWorkbook.Worksheets.Count 'runs through all worksheets
Set wks = ThisWorkbook.Worksheets(k)
For Each cell In wks
If cell.Text = "#REF!" Then
cell.Font.Color = RGB(255, 255, 255)
End If
Next
Next
End Sub
While I disagree with your method of hiding #REF! errors rather than dealing with them so that they are not #REF! errors (or deleting the formulas that are creating them, here is some standard 'loop-through-the-worksheets' code that you should be able to adapt for your purposes.
Sub bad_ref()
Dim w As Long, ref As Range
On Error Resume Next
For w = 1 To Worksheets.Count
With Worksheets(w)
For Each ref In .Cells.SpecialCells(xlCellTypeFormulas, xlErrors)
If ref.Text = "#REF!" Then
ref.Font.ColorIndex = 2
'ref.clear '<~~this clears formatting, formulas. etc from the rogue cell.
End If
Next ref
End With
Next w
End Sub
It should run through quickly enough. Rather than examine every cell on each worksheet, I've narrowed down the cells to be critiqued with the Range.SpecialCells method, looking only through the formulas that produce errors. Something like a #N/A error will be left alone.
I've left an option to actually do something with the errors as a commented line.

Remove reference errors autmatically

i have designed a few codes to help remove reference errors however it does not automatically delete until i assign the macro to a button. i do not want it that way as it would seem unpleasant when i want to present the programme to my team members, and having to remove the errors on the spot with a button. I thought of combining my delete cells code and remove reference cell codes together so that they would run simultaneously but to no avail. Is it possible to combine these two codes to achieve my objective or are there any solutions or coding to remove/hide reference errors automatically? Here are the two codes. All of your help would be very much appreciated!
Sub deletetry2()
Dim R As Range
Set rng = Nothing
On Error Resume Next
Set R = Application.InputBox("Select cells To be deleted", Type:=8)
If TypeName(R) <> "Range" Then
Exit Sub
Else
R.Delete
End If
End Sub
Sub Check_ReferenceDeletecolumn()
Dim rng As Range
Dim rngError As Range
Set rng = Sheets("Sheet3").Range("A1:G100")
On Error Resume Next
Set rngError = rng.Cells.SpecialCells(xlCellTypeFormulas, xlErrors)
On Error GoTo 0
If Not rngError Is Nothing Then
rngError.EntireColumn.Delete
'delete means cells will move up after deleting that entire row
End If
End Sub
If the objective is to remove all rows containing errors, from a user defined range, this should work:
Option Explicit
Public Sub cleanUserDefinedRange()
Dim response As Range
On Error Resume Next
Set response = Application.InputBox("Select range to clean up errors", Type:=8)
If Not response Is Nothing Then cleanUpErrors response
On Error GoTo 0
End Sub
'------------------------------------------------------------------------------------------
Private Sub cleanUpErrors(ByRef rng As Range)
Application.ScreenUpdating = False
rng.SpecialCells(xlCellTypeFormulas, xlErrors).EntireRow.Delete
Application.ScreenUpdating = True
End Sub

Fill in blanks in excel with vba

Im working with a large dataset in excel whose boundaries may change with updates.
I need an automated method to fill in all blank cells with a place holder such as 'n/a'.
Is there a quick way to do that?
Thanks
You will need to loop through cells in your range and wherever you encounter a blank, you'll need the following code
' e.g. you need to make cell A2 read #N/A, i.e. the error value
ActiveSheet.Range("A2").Value = CVErr(xlErrNA)
If you simply need to put the string "N/A" and not the equivalent of the error function =NA() do have a look at the code provided by Gary's Student.
How about:
Sub NoBlanka()
For Each r In Selection
If r.Text = "" Then
r.Value = "n/a"
End If
Next r
End Sub
Select your group of cells and run the macro.
Not 100% clear on requirements, but maybe give this a try. Should be entered into your worksheet code module, and the worksheet name where commented should be changed to whatever name you are using for your sheet.
Private Sub Worksheet_Change(ByVal Target As Range)
Static r As Range
With Worksheets("mySheet")
If .UsedRange Is Nothing Then Exit Sub
If r Is Nothing Then
Set r = .UsedRange
On Error Resume Next
r.SpecialCells(xlBlanks).Value = CVErr(xlErrNA)
On Error GoTo 0
Exit Sub
End If
On Error GoTo ERREUR
If r.Address <> .UsedRange.Address Then
On Error goto 0
Set r = .UsedRange
On Error Resume Next
r.SpecialCells(xlBlanks).Value = CVErr(xlErrNA)
On Error GoTo 0
End If
End With
ERREUR:
Set r = Nothing
End Sub
Not sure if this is what you need. I used a Macro-recorder in Excel.
Sub Macro1()
Cells.Select
Selection.SpecialCells(xlCellTypeBlanks).Select
Selection.FormulaR1C1 = "na"
End Sub
You don't even need vba to do this. You can use the special cells.
In the Home tab of the ribbon, click Find and Select (in the Editing section at the right of the ribbon) and choose Go To Special
Select Blank Cells. Then ok. This will select all your blank cells.
Now type = and your place holder. So for n/a you would simply type ='n/a

How to delete all blank rows

This code makes Excel non-responsive. Anyone know why that might be?
Sub delblank()
On Error Resume Next
ActiveSheet.UsedRange.Range("A:A").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
If Err Then
MsgBox "No blank cells"
End If
End Sub
The problem is that UsedRange won't accept Range("A:A") as a property because the used range in your sheet does not contain an entire column from top to bottom of the Excel sheet, i.e. from row 1 to row 1048756.
What you want instead is to refer to the first column of UsedRange: replace Range("A:A") by Columns(1) like this:
ActiveSheet.UsedRange.Columns(1).SpecialCells(xlCellTypeBlanks).EntireRow.Delete
Now it works.
When you have a long chain of methods and properties giving you trouble like that, it's easier to break it down into its constituents in order to find the source of the error. That's what I did:
Dim r1 As Range
Dim r2 As Range
Dim r3 As Range
Dim sh As Worksheet
Set sh = ActiveSheet
Set r1 = sh.UsedRange
Set r2 = r1.Range("A:A") ' Aha, error occurs here! Wow, that was easy to find.
Set r3 = r1.SpecialCells(xlCellTypeBlanks)
r3.EntireRow.Delete
When the error is gone, it's fine to put the chain back together again to get rid of the clutter.
Also don't use On Error Resume Next unless you're absolutely certain that this is what you want, because it will just swallow errors and not tell you where they came from.
Try something like this:
Public Sub Tester()
On Error Resume Next
Columns(1).SpecialCells(xlBlanks).EntireRow.Delete
On Error GoTo 0
End Sub
Try below code
Sub delblank()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim rng As Range
On Error Resume Next
Set rng = ActiveSheet.UsedRange.Range("A:A").SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
If rng Is Nothing Then
MsgBox "No cells found"
Else
rng.EntireRow.Delete
End If
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub