VBA Paste Cell value if formula contains #REF! Error - vba

When sending out budget files excel files I need to first copy them to a new book then remove the sections that formulas in the document use to get their values. I do this without calculating so #REF errors appear in the formulas but the values do not #REF until i calculate.
I then want to use these #REF errors as an identifier to search for, copy the cells value (pre calculation) and paste as values so I can keep the value.
I have a solution that works below but this takes upwards of ten minutes.
Sub Value_REF2()
'Definitions
Dim lCount As Long
Dim rFoundCell As Range
Dim rng As Range
Dim cell As Range
'If Error go to Process Error function
On Error GoTo ProcError
'Remove any #REF in text first to prevent infinite loop in find below
Set rng = Range("A1:BB999") 'Range may need adjusting based on Grid
'For Loop
For Each cell In rng
If cell.Text = "#REF!" Then
cell.ClearContents
End If
Next cell
' Search for #REFs in forumlas
Set rFoundCell = Range("A1")
Do
Set rFoundCell = Cells.Find(What:="#REF", After:=rFoundCell, _
LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False)
'Take found cell and copy paste values
With rFoundCell
.Copy
.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
End With
Loop 'Error will occur, this will trigger ProcError function
'Process Error Function - Exit Sub
ProcError:
Exit Sub
End Sub
My next solution is below which should be much faster but i just can't work out how to make the IsError look in the actual formula rather than just the cell.
IsError(cell.Formula) does not seem to work?
Sub Value()
Dim rng As Range
Application.ScreenUpdating = False
Application.Interactive = False
Set rng = Range("A1:BB999")
With ActiveSheet
For Each cell In rng
If IsError(cell.Formula) Then
cell.Copy
cell.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False
End If
Next cell
End With
Application.ScreenUpdating = True
Application.Interactive = True
End Sub
The main goal of this is to keep all formulas that do not refer to outside the budget table (like FTE calculations or subtotals and totals) and only value ones that do look outside.
Any Ideas?

Related

Error re-running code

firstly, I'm having error running the code from Excel workbook directly. It leads to the error message mentioned below
We looked at all the data next to your selection and didn't see a pattern for filling in values for you. To use Flash Fill, enter a couple of examples of the output you'd like to see, keep the active cell in the column you want filled in and click the Flash Fill button again
However, I could run the code if is played from VBA windows under the developers tab. But Is limited to only 1 run before an error message 1004 pops up and also code error when played again.
Please help. Never taught or learnt VBA in ever. Code below is a mash up of researched on the net and trial & error.
Sub Graph()
'
' Graph Macro
'
' Keyboard Shortcut: Ctrl+e
'
'Select values in a column from specified workbook and sheet
Dim LR As Long, cell As Range, rng As Range
Windows("Area3-LG").Activate
With Sheets("Graph data")
LR = .Range("B" & Rows.Count).End(xlUp).Row
For Each cell In .Range("B4:B" & LR)
If cell.Value <> "" Then
If rng Is Nothing Then
Set rng = cell
Else
Set rng = Union(rng, cell)
End If
End If
Next cell
'Error with rng.select when Macro is runned again
rng.Select
End With
Selection.Copy
'Open next workbook
Windows("InstData_TEMS_Existing").Activate
'Open Sheet L
Sheets("L").Select
'Select empty field fromn column AA
Range("AA" & Rows.Count).End(xlUp).Offset(1).Select
'paste selection to empty field
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
'Go back to previous workbook & delete column
Windows("Area3-LG").Activate
Sheets("Graph data").Select
Columns("B:B").Select
Application.CutCopyMode = False
Selection.Delete Shift:=xlToLeft
Sheets("Graph Data").Select
End Sub
Thanks in advance (:
Try the code below, without all the unnecessary Select, Activate, and Selection:
Sub Graph()
'
' Graph Macro
'
' Keyboard Shortcut: Ctrl+e
'
'Select values in a column from specified workbook and sheet
Dim LR As Long, cell As Range, rng As Range
With Workbooks("Area3-LG").Sheets("Graph data")
LR = .Range("B" & .Rows.Count).End(xlUp).Row
For Each cell In .Range("B4:B" & LR)
If cell.Value <> "" Then
If rng Is Nothing Then
Set rng = cell
Else
Set rng = Union(rng, cell)
End If
End If
Next cell
End With
rng.Copy ' copy the union range (no need to select it first)
' paste without all the selecting
With Windows("InstData_TEMS_Existing").Sheets("L")
' Paste (without select) un the next empty cell fromn column AA
.Range("AA" & .Rows.Count).End(xlUp).Offset(1).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End With
Application.CutCopyMode = False
'Go back to previous workbook & delete column
Workbooks("Area3-LG").Sheets("Graph data").Columns("B:B").Delete Shift:=xlToLeft
End Sub

Working with the result of a VBA Find in Excel

I've trawled through a few hours of Google looking for an answer for this, so I apologise if it seems obvious to you, it really isn't to me!
I'm trying to take a cell value from 1 workbook, search for it in another. As a result of that, select some data in the search result's row, copy and paste into a cell in the search term's row in the original workbook.
Here's what I've written:
Sub AutoCableSize()
'
' AutoCableSize Macro
Dim Row As Integer
Dim CableRef As String
Dim Rng As Integer
Rng = 0
Row = 1
CableRef = ""
Windows("170615-Submains Cable Schedule.xlsx").Activate
For Each Cell In Range("F3:F303"):
On Error Resume Next
If CableRef = "Finish" Then
GoTo Finish:
End If
CableRef = Range("F" & Row).Value
Windows("170601-B2-3-HL_BAS_SCH_61_0001.xlsx").Activate
Columns("A:A").Select
Selection.Find(What:=CableRef, LookIn:=xlValues _
, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Select
Rng = ActiveCell.Row
If Rng = 1 Then
GoTo Continue
End If
Range("C" & Rng, "D" & Rng).Copy
Windows("170615-Submains Cable Schedule.xlsx").Activate
Range("J" & Row).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Continue:
Row = Row + 1
Next Cell
Finish:
End Sub
What should I put in the Find variables to search for an exact result. I have used xlWhole but I am having an issue:
If the entry does not exist, it skips to the next correctly.
If the entry does exist, it selects the first blank cell in the search series, and treats that as the search result?! I have no idea why!
Try this instead:
Option Explicit
Sub AutoCableSize()
Dim r As Range, findRng As Range
Dim ws1 As Worksheet, ws2 As Worksheet
Set ws1 = Workbooks("170615-Submains Cable Schedule").Worksheets("Sheet1")
Set ws2 = Workbooks("170601-B2-3-HL_BAS_SCH_61_0001").Worksheets("Sheet1")
For Each r In ws1.Range("F3:F303")
Set findRng = ws2.Columns("A:A").Find(What:=r.Value, LookIn:=xlFormulas _
, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not findRng Is Nothing Then
findRng.Copy
ws1.Range("J" & r.Row).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
End If
Next r
End Sub
I've changed your code a lot. The main thing to notice is that I haven't used Activate or Select anywhere. Referring directly to the workbook/worksheet/cell rather than activating it and working with selection is a much better style and it's the first thing to learn if you want to write error-free code.
More here: How to avoid using Select in Excel VBA macros
#CallumDA
In looking at your answer code, I found that in recent Excel versions, it DID NOT FUNCTION correctly - UNLESS you used exactly what the Macro recorder creates:
Set X = {AnyRangeVariableHere}.Find(...) method calls fail universally to return an object instance now, leaving X = Nothing (and failing to find a target, even when a valid one exists).
The only syntax which seems to work is literally:
Set X = Cells.Find(...)
If you look it all up the documents, this distinction makes no sense, but I can assure you that with Excel 2016/2019, this certainly seems to be the case.
Apparently Application.Cells method is some sort of special case/subclass whereby the .Find method actually still functions and returns a range object reference.

Why won't my range paste?

The following is my VBA code, for some reason the code will run but not actually paste in the range I need it to paste. Anybody have any ideas why it won't paste my values?
The programs goes to my selected cell that I'm looking for, but now the activecell becomes my range and I'm trying to paste the it there. Any information will help, it just doesn't want to paste the values in the range I selected.
Sub Macro1()
Dim Form1033 As Worksheet
Dim CleaningSchedule As Worksheet
Set Form1033 = Worksheets("Form1033andForm1034")
Set CleaningSchedule = Worksheets("CleaningSchedule")
Dim Day As Range
Set Day = Form1033.Range("$J$3")
With Form1033
Range("$G$5:$G$18").Select
Selection.Copy
End With
With CleaningSchedule
Dim i As Integer
For i = 6 To 37
If Cells(4, i).Value = Day.Value Then
Cells(5, i).Select
Range(ActiveCell, Cells(ActiveCell.Rows + 13, ActiveCell.Column)).Select
Selection.PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End If
Next i
End With
Form1033.Select
Application.CutCopyMode = False
Range("$G$5:$G$18").ClearContents
MsgBox "Scoresheet Updated"
End Sub
I fixed the code here, but please read the link I provided in my comment, and you will not have these errors in the future.
I also commented the refactors I did to the code. Also, notice that I assigned the Cells and Ranges to the parent worksheet with .. (See #BruceWayne's link in his comment to your original question)
Sub Macro1()
Dim Form1033 As Worksheet
Dim CleaningSchedule As Worksheet
Set Form1033 = Worksheets("Form1033andForm1034")
Set CleaningSchedule = Worksheets("CleaningSchedule")
Dim Day As Range
Set Day = Form1033.Range("$J$3")
'copy the range directly
Form1033.Range("$G$5:$G$18").Copy
With CleaningSchedule
Dim i As Integer
For i = 6 To 37
If .Cells(4, i).Value = Day.Value Then
'paste directly to range and i also combined 13 rows plus row 5, since you are always using the same row
Range(.Cells(5,i), Cells(18,i)).PasteSpecial Paste:=xlPasteFormulas, Operation:=xlNone, _
SkipBlanks:=False, Transpose:=False
End If
Next i
End With
'clear contenst directly
Form1033.Range("$G$5:$G$18").ClearContents
MsgBox "Scoresheet Updated"
End Sub
Since you are using "With" statement, you need to add a "." in front of "cells" and "range" and any other references you make. For example:
With myWorksheet
.range("A1").copy
End with
So, the problem in this case is that you still remain on the same worksheet and clear the contents of the cells you had pasted.

Run time error in excel vba. last used range rows selects row 65536 instead of actual last used range

I am trying to select columns E and K from sheet Input, process in Working sheet and paste in the Output sheet after the last used row. I have stored the last used row number in x and paste the values in x+1 cell. However excel selects last row of the sheet (x as 65536) and gives run time error 4004. Can someone please help me in assisting the code.
Dim x As Long, y As String
Sheets("Input").Activate
Range("E:E,K:K").Select
Range("K1").Activate
Selection.Copy
Sheets("Working").Select
Cells(1, 1).Select
ActiveSheet.Paste
Cells.Select
Application.CutCopyMode = False
Selection.AutoFilter
Range("B5").Select
ActiveSheet.Range("$A$1:$H$30").AutoFilter Field:=1, Criteria1:="="
Cells.Select
Selection.Delete Shift:=xlUp
Columns("B:B").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("B2").Select
ActiveCell.FormulaR1C1 = _
"=IF(RC[-1]="""","""",VLOOKUP(RC[-1],Repository!C[-1]:C[1],3,0))"
Range("B2").Select
Selection.Copy
Range("B3:B30").Select
ActiveSheet.Paste
Cells.Select
Application.CutCopyMode = False
Selection.Copy
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("B2:C2").Select
Range(Selection, Selection.End(xlDown)).Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Output").Select
Range("A1").Select
x = Worksheets("Output").UsedRange.Rows.Count
y = "a" & Trim(x + 1)
ActiveSheet("Output").Range(y).Select
ActiveSheet.Paste
Your UsedRange is still thinking that the last row is 65536. Add this subroutine, then call it right before you set x.
Sub CorrectUsedRange()
Dim values
Dim usedRangeAddress As String
Dim r As Range
'Get UsedRange Address prior to deleting Range
usedRangeAddress = ActiveSheet.UsedRange.Address
'Store values of cells to array.
values = ActiveSheet.UsedRange
'Delete all cells in the sheet
ActiveSheet.Cells.Delete
'Restore values to their initial locations
Range(usedRangeAddress) = values
End Sub
Near the bottom of your code replace:
Sheets("Output").Select
with:
Sheets("Output").Select
ActiveSheet.UsedRange
this should "re-set" UsedRange
Sometimes the Used Range gets generically large and won't reset on it's own. When this happens, the only way that I've found to force it to reset itself correctly is to Save the Workbook that the subject Worksheet is in. This works for me, on Excel 2010 anyway. Since you're using .Select and Active<obj> (which I don't recommend), it would simply be this:
ActiveWorkbook.Save
I would use a Find loop to populate an array and then output the array when the macro has completed. There is no need for a "Working" sheet. This also uses Cells(Rows.Count, "A").End(xlUp) in order to find the last populated row instead of UsedRange.Rows.Count which can be unreliable.
Sub tgr()
Dim rngFound As Range
Dim rngLookup As Range
Dim arrResults() As Variant
Dim ResultIndex As Long
Dim strFirst As String
With Sheets("Input").Columns("E")
Set rngFound = .Find("*", .Cells(.Cells.Count), xlValues, xlWhole)
If Not rngFound Is Nothing Then
strFirst = rngFound.Address
ReDim arrResults(1 To WorksheetFunction.CountA(.Cells), 1 To 2)
Do
If rngFound.Row > 1 Then
ResultIndex = ResultIndex + 1
On Error Resume Next 'Just in case the VLookup can't find the value on the 'Repository' sheet
arrResults(ResultIndex, 1) = Evaluate("VLOOKUP(""" & rngFound.Value & """,Repository!A:C,3,FALSE)")
arrResults(ResultIndex, 2) = .Parent.Cells(rngFound.Row, "K").Value
On Error GoTo 0 'Remove the On Error Resume Next condition
End If
Set rngFound = .Find("*", rngFound, xlValues, xlWhole)
Loop While rngFound.Address <> strFirst
End If
End With
If ResultIndex > 0 Then Sheets("Output").Cells(Rows.Count, "A").End(xlUp).Offset(1).Resize(ResultIndex, UBound(arrResults, 2)).Value = arrResults
Set rngFound = Nothing
Erase arrResults
End Sub
instead of used range check how many rows already are filled with this code:
X = WorksheetFunction.CountA(Columns(1))
Of course this only works ok if you have no rows that are empty in Column A, as those rows would be ignored!

Extract tabular data from every Excel tab, and paste data on a single sheet

I have an excel spreadsheet with 75 tabs-- each tab is formatted in the same way with two columns of words. I want all of this data to be on just a single page, but I don't know how to programmatically extract tables from each tab and paste it on a single tab.
Is there a way to do this in Excel?
Alright, here's the code that I've tried:
Sub Macro5()
Range("A1:B30").Select
Selection.Copy
Sheets("Table 1").Select
Selection.End(xlDown).Select
Selection.PasteSpecial Paste:=xlPasteAllUsingSourceTheme, Operation:=xlNone _
, SkipBlanks:=False, Transpose:=False
End Sub
All tabs are formatted in the same way, with data in all cells from A1:B30. I'm thinking that the Selection.End command would go to the next available open cell and paste data from the subsequent tabs in that.
As of current, I would need to go to each tab and individually run this macro, except that it doesn't work because it says the pasted data is not of the same type/format of the existing data.
Any ideas?
Coding attempt #2- SUCCESS!!!
Sub Macro5()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
ws.activate
Range("A1:B30").Select
Selection.Copy
Sheets("Table 1").Select
Selection.End(xlDown).Select
Selection.PasteSpecial Paste:=xlPasteAllUsingSourceTheme, Operation:=xlNone _
, SkipBlanks:=False, Transpose:=False
On Error Resume Next 'Will continue if an error results
Next ws
End Sub
Well, I hate to admit I'm glad you didn't just spoonfeed me the answer. Good on you, sir.
Coding Attempt #3- Avoid Selections
Sub Macro5()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
Set Rng = ws.Range("A1:B30")
Rng.Copy
Dim ws1 As Worksheet
Set ws1 = Worksheets("Table 1")
ws1.Select
Selection.End(xlDown).Select
Selection.PasteSpecial Paste:=xlPasteAllUsingSourceTheme, Operation:=xlNone _
, SkipBlanks:=False, Transpose:=False
On Error Resume Next 'Will continue if an error results
Next ws
End Sub
Not quite right-- it still works, but I'm not sure how to avoid using "Selection" when I get to the first workbook. Is there a way to reference the most proximate cell without content? I know the 'End' key can do this, but is there a non-selection based way?
See this code.
I modified your code so that it doesn't use .Select or .Activate at all.
I have commented the code so you shouldn't have a problem understanding it. :)
The code doesn't use On Error Resume Next. You should always avoid that unless it is necessary. Use proper error handling instead. Consider On Error Resume Next as telling your application to simply SHUT UP. :)
Here is an example of basic error handling
Sub Sample()
On Error GoTo Whoa
'
'~~> Rest of Code
'
Exit Sub
Whoa:
MsgBox Err.Description
End Sub
So this is how your final code will look like. It avoids the use of .Select or .Activate. It also avoids the use of Selection and finds the exact range that needs to be copied and exact range where it needs to be copied. Also it does proper error handling.
Option Explicit
Sub Sample()
Dim wsInput As Worksheet, wsOutput As Worksheet
Dim rng As Range
Dim LRowO As Long, LRowI As Long
On Error GoTo Whoa
'~~> Set your Output Sheet
Set wsOutput = ThisWorkbook.Sheets("Table 1")
'~~> Loop through all sheets
For Each wsInput In ThisWorkbook.Worksheets
'~~> Ensure that we ignore the output sheet
If wsInput.Name <> wsOutput.Name Then
'~~> Working with the input sheet
With wsInput
'~~> Get the last row of input sheet
LRowI = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Set your range for copying
Set rng = .Range("A1:B" & LRowI)
'~~> Copy your range
rng.Copy
'~~> Pasting data in the output sheet
With wsOutput
'~~> Get the next available row in output sheet for pasting
LRowO = .Range("A" & .Rows.Count).End(xlUp).Row + 1
'~~> Finally paste
.Range("A" & LRowO).PasteSpecial Paste:=xlPasteAllUsingSourceTheme, _
Operation:=xlNone, SkipBlanks:=False, Transpose:=False
End With
End With
End If
Next wsInput
Exit Sub
Whoa:
MsgBox Err.Description
End Sub