Excel VBA 1004 Error with named range formula - vba

I am tinkering with a program but I get an error that I cant place.
Error is 1004
Code:
Source_1_Criteria = "Factuur"
Source_1_Name = Range("MDM_MDM_Tool_List").Find(what:=Source_1_Criteria).Offset(0, 2).Value
Source_1_Area = Range("MDM_MDM_Tool_List").Find(what:=Source_1_Criteria).Offset(0, 4).Value
ActiveWorkbook.Names.Add Name:=Source_1_Name, RefersTo:=Source_1_Area
The last line gives me the error.
I know its something in the 3 line.
The result in the 3 line results in error with current contents in the cell
=VERSCHUIVING(archief!$A$2;0;0;1;AANTALARG(archief!$A$2:archief!$Y$2))
When I change the contents to =archief!$A$2 the code works.
But when I manually copy/paste the formula in a named range it works....
So to my calculation:
Formula = Works
Code = Works
those 2 together gives
error 1004

The formula you should have in place of =VERSCHUIVING(archief!$A$2;0;0;1;AANTALARG(archief!$A$2:archief!$Y$2)) should be this one:-
=ADRES(2;1;;;"Archief") & ":" & ADRES(2;AANTALARG(Archief!$A$2:$Y$2))
Below is code which I tested thoroughly. It works with the formula I supplied yesterday (above). You can use Edit/Replace to restore your original variable names in the code below.
Option Explicit
Private Sub Dutch()
' 05 Jan 2018
Dim Wb As Workbook
Dim Ws As Worksheet
Dim SrcCrit As String
Dim SrcName As Variant
Dim SrcArea As String
Dim Sp() As String
Dim Rng As Range
Set Wb = ActiveWorkbook ' could be ThisWorkbook
Set Ws = ActiveSheet ' could be another sheet
SrcCrit = "Factuur"
Set Rng = Ws.Range("ToolList").Find(What:=SrcCrit)
If Not Rng Is Nothing Then
SrcName = Rng.Offset(0, 2).Value
' Debug.Print SrcName
SrcArea = Rng.Offset(0, 4).Value
' Debug.Print SrcArea
Sp = Split(SrcArea, "!")
If UBound(Sp) Then
If Left(Sp(0), 1) = "'" Then Sp(0) = Mid(Sp(0), 2)
If Right(Sp(0), 1) = "'" Then Sp(0) = Left(Sp(0), Len(Sp(0)) - 1)
Else
Sp = Split(Ws.Name & "|" & Sp(0), "|")
End If
Set Rng = Wb.Worksheets(Sp(0)).Range(Sp(1))
' Debug.Print Rng.Worksheet.Name, Rng.Address
On Error Resume Next
Wb.Names(SrcName).Delete
On Error GoTo 0
Wb.Names.Add Name:=SrcName, RefersTo:=Rng
' With Wb.Names(SrcName)
' Debug.Print .Name, .RefersTo
' End With
End If
End Sub
I found that error 1004 is a little complicated in the above context because such an error will occur when a faulty Name exists in the workbook, such as may have been created by your original code. In that case the error indicates the existence of an unrecognised element in the workbook which may be totally unrelated to the code you are intending to test run. 1004 errors of this nature are commonly caused by compatibility issues, not even related to names. Google for _xlfn IERROR
I got a persistent 1004 error while developing the above code because I had named the offsets from the found range as "first" to "fourth" which allocated "second" to the new named range. Wb.Names("Second").Name correctly returned "second" but Wb.Names(ScrName).Name trigered error 1004 - just your complaint. The error went away when I found out that SECOND() is a worksheet function and changed the name in the offset cell.

Related

How do I copy multiple ranges to the clipboard?

I have searched for this answer and many answers are close, but none of them are exactly what I need.
I need to copy the contents of 3 ranges into the clipboard and I've tried different methods to no avail. Below is an example of my code and one method...
Sub COPY_UPDATES()
Dim CopyPart1 As Range, CopyPart2 As Range, CopyPart3 As Range, AllParts As Range
Set CopyPart1 = Range("$B$2:$B$14").SpecialCells(xlCellTypeConstants)
Set CopyPart2 = Range("$A$2:$A$10000").SpecialCells(xlCellTypeConstants)
Set CopyPart3 = Range("$B$17")
Set AllParts = Union(CopyPart1, CopyPart2, CopyPart3)
'Set AllParts = Range("CopyPart1.Address, CopyPart2.Address, CopyPart3.Address")
'Set AllParts = Range(CopyPart1, CopyPart2, CopyPart3)
'Debug.Print CopyPart1 'Run-time error '13': Type mismatch
'Debug.Print CopyPart2 'Run-time error '13': Type mismatch
Debug.Print CopyPart3 'Immediate prints the value I need
'Debug.Print AllParts 'Run-time error '13': Type mismatch
AllParts.Copy 'Run-time error '1004': This action won't work on multiple selections
MsgBox "SQL UPDATES have been copied to the Clipboard." & vbNewLine & _
"Paste into SMSS (New Query) and Execute (F5)"
End Sub
The only Debug.Print that does NOT set off an error is CopyPart3, the single cell range. Taking SpecialCells out of the other two doesn't seem to matter either. I seen other cases talk about setting up a loop, but is that something I need?
Again, I have searched and searched and just cannot find this particular solution.
I just want those ranges stacked up so I can paste them all at once. Thanks!
I got what I needed using #Variatus's suggestion from the comments in my original post, which is basically copy and pasting each range into another range first, then copy that new range of combined ranges as one. Below is my new code with factors in there to clean out empty cells and format.
Sub COPY_UPDATES()
Dim CopyPart1 As Range, CopyPart2 As Range, CopyPart3 As Range, AllParts As Range
Dim CP2_LRow As Long
CP2_LRow = Cells(Rows.Count, 1).End(xlUp).Row
Set CopyPart1 = Range("$B$2:$B$12")
Set CopyPart2 = Range("$A$2:$A$" & CP2_LRow)
Set CopyPart3 = Range("$B$14")
CopyPart1.Copy
Range("$B$20:$B$32").PasteSpecial xlPasteValues 'removes cell format on paste
CopyPart2.Copy
Range("$B$33:$B$" & CP2_LRow + 32).PasteSpecial xlPasteValues 'removes cell format on paste
CopyPart3.Copy
Range("$B$" & CP2_LRow + 32 + 1).PasteSpecial xlPasteValues 'removes cell format on paste
Set AllParts = Range("$B$20:$B$" & CP2_LRow + 32 + 1)
'below copies the 3 pasted ranges over one column to remove blanks from CopyParts pastes
AllParts.SpecialCells(xlCellTypeConstants).Copy Destination:=Range("$C$20")
'delete the original 3 CopyPart pastes to shift newly copy/pasted values with no blanks to AllParts range
AllParts.Delete Shift:=xlToLeft
Dim AP_LRow As Long, AllPartsFinal As Range
AP_LRow = Cells(Rows.Count, 2).End(xlUp).Row
Set AllPartsFinal = Range("$B$20:$B$" & AP_LRow)
'last copy to clipboard to be pasted
AllPartsFinal.Copy
MsgBox "SQL UPDATES have been copied to the Clipboard." & vbNewLine & _
"Paste into SMSS (New Query) and Execute (F5)"
End Sub

I need help looping an index/match that is inside an if statement using Excel VBA

I am trying to create a VBA macro to fill in cells that are empty in a range ("INV_Nums") without overwriting the cell if it contains data already. In order to accomplish this I am trying to use an if statement to check if the cell in question is blank...if it is not, then I want the loop to continue on to the next cell, however if it IS blank then I want to input the index(__,(match()) function into the cell.
I keep getting a "compile error: mismatch" on the True statement but I'm at a loss as to why my synatax would be wrong. Any help would be appreciated.
Here is my code:
Dim i As Integer
Dim Rng As Range
Dim ARwkb As Excel.Workbook
Dim ARwks As Excel.Worksheet
Dim Samwkb As Excel.Workbook
Dim Samwks As Excel.Worksheet
Set Samwkb = Excel.Workbooks("Samples - one sheet")
Set Samwks = Samwkb.Worksheets("samples shipment")
Set ARwkb = Excel.Workbooks("AR balance.xlsx")
Set ARwks = ARwkb.Worksheets("Total Trading")
Set Rng = Samwkb.Range("INV_Nums")
For i = 6 To Rng.Rows.Count + 6
If Range("AAi") = "" Is True Then
Range("AAi").Select
ActiveCell.FormulaR1C1 = _
"=INDEX('AR balance.xlsx'!AR_Invoice_Nums,MATCH(RC[-21],'AR
balance.xlsx'!AR_PL_Nums,0))"
End If
Next i
The problem is in how you are identifying the range and administering the criteria.
For i = 6 To Rng.Rows.Count + 6
If IsEmpty(Range("AA" & i)) Then
Range("AA" & i).FormulaR1C1 = _
"=INDEX('AR balance.xlsx'!AR_Invoice_Nums, MATCH(RC[-21],'AR balance.xlsx'!AR_PL_Nums, 0))"
End If
Next i
The .SpecialCells method can quickly determine the blank cells and an xlR1C1 formula can be used to insert all of the formulas at once..
...
with Samwkb.Range("INV_Nums")
.specialcells(xlcelltypeblanks).FormulaR1C1 = _
"=INDEX('AR balance.xlsx'!AR_Invoice_Nums, MATCH(RC[-21],'AR balance.xlsx'!AR_PL_Nums, 0))"
end with
...

Look up values in sheet(x) column(x), match to values in sheet(y) column(y), if they match paste row

Dealing with an issue that seems simple enough, but for some reason I cannot get this to work.
I have a data input sheet I am trying to match values across to another sheet, the values are both in column E, and all the values in column E are unique.
The values will always be stored in rows 8 though to 2500.
My code is as below, however is throwing the ever useful 1004 error (Application-Defined or object-defined error), on line
If Sheets("Target Inputs").Range("E" & CStr(LSearchRow)).Value = searchTerm Then
any help would be greatly appreciated:
Sub LOAD_BUID_Lookup()
Dim i As Integer
Dim LSearchRow As Integer
Dim LCopyToRow As Integer
Dim searchTerm As String
On Error GoTo Err_Execute
For i = 8 To 2500
searchTerm = Range("E" & i).Text
If Sheets("Target Inputs").Range("E" & CStr(LSearchRow)).Value = searchTerm Then
'Select row in Sheet1 to copy
Rows(CStr(LSearchRow) & ":" & CStr(LSearchRow)).Select
Selection.Copy
'Paste row into Sheet2 in next row
Sheets("LOAD").Select
Rows(CStr(LCopyToRow) & ":" & CStr(LCopyToRow)).Select
ActiveSheet.Paste
'Move counter to next row
LCopyToRow = LCopyToRow + 1
'Go back to Sheet1 to continue searching
Sheets("Target Inputs").Select
End If
Next i
Application.CutCopyMode = False
Range("A3").Select
MsgBox "All matching data has been copied."
Exit Sub
Err_Execute:
MsgBox "An error occurred."
End Sub
LSearchRow is not being set to any value, which means it is 0. This in turn throws the exception, since the row number cannot be 0. And there is no reason whatsoever to convert to string with CStr, since the concatenation casts the entire range parameter to a string anyway.
Usually when comparing two different columns in two different sheet you would see a double loop the first to loop through sheet1 and the second to take every value of sheet1 and loop through sheet2 to find a match. In reading your description I think this is what you want.
Double loops can be time intensive. There is another way, Worksheetfunction.match!!
I also noticed your code selecting/activating sheets multiple times. Typically selecting/activating sheets is not required if you declare and instantiate the variables you need.
Below is an example code I tried to make it as plug and play as possible, but I wasn't sure of the name of the sheet you are looping through. I've tested the code on dummy data and it seems to work, but again I'm not quite positive on the application. I've commented the code to explain as much of the process as possible. Hopefully it helps. Cheers!
Option Explicit 'keeps simple errors from happening
Sub LOAD_BUID_Lookup()
'Declare variables
Dim wb As Workbook
Dim wsInputs As Worksheet
Dim wsTarget As Worksheet
Dim wsLoad As Worksheet
Dim searchTerm As String
Dim matchRng As Range
Dim res
Dim i As Integer
'instantiate variables
Set wb = Application.ThisWorkbook
Set wsInputs = wb.Worksheets("Inputs") 'unsure of the name of this sheet
Set wsTarget = wb.Worksheets("Target Inputs")
Set wsLoad = wb.Worksheets("LOAD")
Set matchRng = wsTarget.Range("E:E")
On Error GoTo Err_Execute
For i = 8 To 2500
searchTerm = wsInputs.Range("E" & i).Text 'can use sheet variable to refer exactly to the sheet you want without selecting
'get match if one exists
On Error Resume Next
res = Application.WorksheetFunction.Match(searchTerm, matchRng, 0) 'will return a row number if there is a match
If Err.Number > 0 Then 'the above command will throw an error if there is no match
'MsgBox "No Match!", vbCritical
Err.Clear ' we clear the error for next time around
On Error GoTo 0 'return to previous error handeling
Else
On Error GoTo 0 'return to previous error handeling
wsInputs.Range("A" & i).EntireRow.Copy Destination:=wsLoad.Range("A" & wsLoad.Range("E50000").End(xlUp).Row + 1) 'gets last row and comes up to last used row ... offset goes one down from that to the next empty row
End If
Next i
'Application.CutCopyMode = False -- there is no need for this when we use "Destination"
MsgBox "All matching data has been copied."
Exit Sub
Err_Execute:
MsgBox "An error occurred."
End Sub

VBA Named Range most efficient way to check if name exists

I have a routine, that fills a calendar with all important events for the commodity markets for each day of the following week. I have a calendar grid laid out on the page and have ten named cells for each day i.e. Monday1, Monday2 and so on (each day only goes up to 10 for now, i.e.Monday10), in each days column. BTW the cells are 2 cells wide and 2 cells deep. Many times there are more than 10 events for a given day. I am trying to test for the named range to see if it exists, if not copy the format of the last named range cell and name that cell the next name in the series.
I am only having two issues with the above, first and foremost is how to test to determine in a name for a named range already exists. I am currently iterating thru the entire list of ThisWorkbook.Names, which has thousands of named ranges in it. Since this iteration could be running over 100 times when the calendar is generating, it is wicked slow (as would be expected). Is there a better, faster way to check if a name already exists as a named range?
The second issue is how to copy the formatting of a 4 cell, merged cell, since the address always comes up as only the top left corner cell so offsetting the range doesn't work appropriately. I hacked around to get this code to at least come up with the right range for the next merged cell group in the column
Set cCell = Range("Thursday" & CStr(y))
'even tho cCell is a 4 cell merged cell, cCell.Address returns the address of top left cell
Set destRange = Range(cCell.Address & ":" & cCell.offset(2, 0).offset(0, 1).Address)
Recording a macro to drag the formatting down, shows this code.
Range("G22:H23").Select
Selection.AutoFill Destination:=Range("G22:H25"), Type:=xlFillFormats
Range("G22:H25").Select
Since Range("G22:H23") is the same as cCell, and Range("G22:H25") is the same as destRange. The following code should work, but doesn't.
Set cCell = Range("Thursday" & CStr(y))
Set destRange = Range(cCell.Address & ":" & cCell.offset(2, 0).offset(0, 1).Address)
cCell.AutoFill Destination:=destRange, Type:=xlFillFormats
Application.CutCopyMode = False
cCell.offset(1, 0).Name = rangeName
FYI, it doesn't work if I select cCell and use Selection.AutoFill either.
Any thoughts on how to copy that cell formatting down the column one cell at a time when needed?
Update:
This now works for copying the formatting down from one merged cell to another of same size. For some reason setting destRange to the whole range (the copy cell and pastecell entire range as the macro recorder showed) didnt work but setting destRange to the cell range that needed formatting, and then doing a union of cCell and destRange worked, and made naming the new range easier.
rangeName = "Friday" & CStr(y + 1)
priorRangeName = "Friday" & CStr(y)
namedRangeExist = CheckForNamedRange(rangeName)
If namedRangeExist = False Then
Set cCell = Range(priorRangeName)
Set destRange = Range(cCell.offset(1, 0).Address & ":" & cCell.offset(2, 0).offset(0, 1).Address)
cCell.AutoFill Destination:=Union(cCell, destRange), Type:=xlFillFormats
Application.CutCopyMode = False
destRange.Name = rangeName
End If
Update #2
There is an issue with naming ranges in a For loop ( the code below is running inside a For loop). The first time the new rangeName is not found, Setting cCell to the prior range name and running through the code to copy the merged cell format and name the new range works fine. Here is the code
rangeName = "Thursday" & CStr(y + 1)
priorRangeName = "Thursday" & CStr(y)
namedRangeExist = DoesNamedRangeExist(rangeName)
If namedRangeExist = False Then
Set cCell = Range(priorRangeName)
Debug.Print "cCell:" & cCell.Address
Set cCell = cCell.MergeArea
Debug.Print "Merged cCell:" & cCell.Address
Set destRange = Range(cCell.offset(1, 0).Address & ":" & cCell.offset(2, 0).offset(0, 1).Address)
Debug.Print "Dest:" & destRange.Address
Debug.Print "Unioned:" & Union(cCell, destRange).Address
cCell.AutoFill Destination:=Union(cCell, destRange), Type:=xlFillFormats
Application.CutCopyMode = False
destRange.name = rangename
End If
results in the following ranges
cCell:$G$22
Merged cCell:$G$22:$H$23
Dest:$G$24:$H$25
Unioned:$G$22:$H$25
but if more than one new named range needs to be created the second time thru this code produces a range area as evidenced by the output shown below
cCell:$G$24:$H$25
so why does cCell's address show as only the upper left cells address when run the first time, but the second time thru cCell's address is shown as the whole merged cell range? And because it does, the next code line produces a range object error
Set cCell = cCell.MergeArea
Eliminating that code line and amending the first Set cCell to this;
Set cCell = Range(priorRangeName).MergeArea
produces the same error. I could kludge this by setting a counter, and if more than one, bypass that code line but that is not the preferred solution.
First and foremost, create a function to call the named range. If calling the named range generate an error the function will return False otherwise it will return True.
Function NameExist(StringName As String) As Boolean
Dim errTest As String
On Error Resume Next
errTest = ThisWorkbook.Names(StringName).Value
NameExist = CBool(Err.Number = 0)
On Error GoTo 0
End Function
As for your second question, I had not problem with the autofill.
I would replce Set destRange = Range(cCell.Address & ":" & cCell.offset(2, 0).offset(0, 1).Address) with Set destRange = cCell.Resize(2,1). It has the same effect but the later is much cleaner.
Application.Evaluate and Worksheet.Evaluate can be used to get error value instead of error :
If Not IsError(Evaluate("Monday1")) Then ' if name Monday1 exists
The error can be ignored or jumped over (but that can result in hard to detect errors) :
On Error GoTo label1
' code that can result in error here
label1:
If Err.Number <> 0 Then Debug.Print Err.Description ' optional if you want to check the error
On Error GoTo 0 ' to reset the error handling
Range.MergeArea can be used to get the Range of merged cell.
I created a function to extend the name ranges and fill in the formatting. The first named range in the series will have to be setup. The Name itself needs to be set to the top left cell in the merged area.
ExtendFillNamedRanges will calculate the positions of the named ranges. If a cell in one of the positions isn't part of a MergedArea it will fill the formatting down from the last named range. It will name that cell. The scope of the names is Workbook.
Sub ExtendFillNamedRanges(BaseName As String, MaxCount As Integer)
Dim x As Integer, RowCount As Integer, ColumnCount As Integer
Dim LastNamedRange As Range, NamedRange As Range
Set NamedRange = Range(BaseName & 1)
RowCount = NamedRange.MergeArea.Rows.Count
ColumnCount = NamedRange.MergeArea.Columns.Count
For x = 2 To MaxCount
Set NamedRange = NamedRange.Offset(RowCount - 1)
If Not NamedRange.MergeCells Then
Set LastNamedRange = Range(BaseName & x - 1).MergeArea
LastNamedRange.AutoFill Destination:=LastNamedRange.Resize(RowCount * 2, ColumnCount), Type:=xlFillDefault
NamedRange.Name = BaseName & x
End If
'NamedRange.Value = NamedRange.Name.Name
Next
End Sub
Here is the test that I ran.
Sub Test()
Application.ScreenUpdating = False
Dim i As Integer, DayName As String
For i = 1 To 7
DayName = WeekDayName(i)
Range(DayName & 1).Value = DayName & 1
ExtendFillNamedRanges DayName, 10
Next i
Application.ScreenUpdating = True
End Sub
Before:
After:
I found this on ozgrid and made a little function out of it:
Option Explicit
Function DoesNamedRangeExist(VarS_Name As String) As Boolean
Dim NameRng As Name
For Each NameRng In ActiveWorkbook.Names
If NameRng.Name = VarS_Name Then
DoesNamedRangeExist = True
Exit Function
End If
Next NameRng
DoesNamedRangeExist = False
End Function
You can put this line in your code to check:
DoesNamedRangeExist("Monday1")
It will return a Boolean value (True / False) so it's easy to use with an IF() statement
As to your question on merged cells, I did a quick macro record on a 2*2 merged cell and it gave me this (made smaller and added comments):
Sub Macro1()
Range("D2:E3").Copy 'Orignal Merged Cell
Range("G2").PasteSpecial xlPasteAll 'Top left of destination
End Sub

Program goes straight to else statement, then produces Run time error '9' subscript out of range

I'm new to VBA and am trying to design a program that will go through a column with Strings in it and for every unique String name create a new worksheet object with that String value as its name and then copy and paste the values in that row to the new sheet. All identical Strings should then also have the values in their row copied over to the new sheet. I'm not even sure if the program itself works, but before I can check I keep getting an error that I haven't been able to fix.
The error is run time error '9' subscript out of range.
The thing is the new sheet is getting created but is not getting filled up with any data. It's as if the program goes straight to the else statement and then finds an error that I'm not sure how to fix even though it should be going through the If statement at least once because I know that the String in A3 is the same as that in A2. Here's the full code:
Sub FilterByClass()
Dim i As Long
Dim j As Long
Dim sheetName As String
ActiveSheet.Name = "AllClasses"
sheetName = Worksheets("AllClasses").Cells(2, 1).Value
Worksheets.Add
ActiveSheet.Name = sheetName
Worksheets("AllClasses").Activate
Worksheets(sheetName).Rows(1) = ActiveSheet.Rows(1)
Worksheets(sheetName).Rows(2) = ActiveSheet.Rows(2)
j = 3
For i = 3 To Rows.Count
If Cells(i, 1).Value = Cells(i - 1, 1).Value Then
Worksheets(Cells(i, 1).Value).Rows(j) = ActiveSheet.Rows(i)
j = j + 1
Else
Worksheets.Add
ActiveSheet.Name = ThisWorkbook.Sheets(sheetName).Cells(i, 1).Value
Worksheets("AllClasses").Activate
j = 1
Worksheets(Cells(i, 1).Value).Rows(j) = ActiveSheet.Rows(1)
j = j + 1
Worksheets(Cells(i, 1).Value).Rows(j) = ActiveSheet.Rows(i)
End If
Next i
End Sub
Any help would be appreciated. And if you see anything in the rest of the code that looks like it clearly won't work as intended please point it out as well. Thanks
Before you name a worksheet, check if the sheet exists like David mentioned in the comments.
Here is my favorite way of checking if the sheet exists
Sub Sample()
If DoesSheetExist("AllClasses") Then
MsgBox "Sheet Already Exists"
Else
ActiveSheet.Name = "AllClasses"
End If
End Sub
Function DoesSheetExist(Sh As String) As Boolean
Dim ws As Worksheet
On Error Resume Next
Set ws = ThisWorkbook.Sheets(Sh)
On Error GoTo 0
If Not ws Is Nothing Then DoesSheetExist = True
End Function
Also if the sheet doesn't exist then it may be possible that the workbook is protected. To check if that is the case, you can use the below code
If ThisWorkbook.ProtectStructure = True Then
MsgBox "Workbook structure is protected"
Else
MsgBox "Workbook structure is not protected"
End If