I have a workbook with many named ranges to rename. I have a spreadsheet with the old names and the new names.
This works:
Dim strOldName As String
Dim strNewName As String
strOldName = rngNamedRanges.Cells(1, 6).Value2
strNewName = strOldName & "_Renamed"
With ActiveWorkbook.Names(strOldName)
.Name = strNewName
End With
This does not:
Dim strOldName As String
Dim strNewName As String
strOldName = rngNamedRanges.Cells(1, 6).Value2
strNewName = CStr(rngNamedRanges.Cells(1, 8).Value2)
With ActiveWorkbook.Names(strOldName)
.Name = strNewName
End With
Clearly, I'm doing something wrong assigning strNewName.
I have also tried using .text, .value, and trimming the string, all with the same non-result.
The non-working code does not produce an error. It just fails to change the name.
rngNamedRanges.Cells(1,6) refers to a cell containing straight text.
rngNamedRanges.Cells(1,8) refers to a cell containing a CONCATENATE formula which creates the new range name based on several other pieces of info contained in other columns.
Renaming is always a pain. Try the following:
Sub Rename()
StrOld = "MyRange1"
StrNew = StrOld & "_Renamed"
Range(StrOld).Name = StrNew
With ThisWorkbook
.Names(StrOld).Delete
End With
End Sub
Looping is up to you. :) Let us know if this helps.
Thanks for the input, all! I still don't understand why the first example I gave worked and the second one did not. Nonetheless, the following code appears to be working. I apologize for poor formatting of the snippet.
Dim rngNamedRanges As Range
Dim strOldName As String
Dim strNewName As String
Dim strRefersTo As String
Set rngNamedRanges = ActiveWorkbook.Worksheets("Named Ranges").Range("A2:K909")
i = 1
Do Until [CONDITION] = ""
strOldName = CStr(Trim(rngNamedRanges.Cells(i, 6).Value2))
strNewName = CStr(Trim(rngNamedRanges.Cells(i, 8).Value2))
strRefersTo = ActiveWorkbook.Names(strOldName).RefersTo
'Update all the formulas to use the new name.
For Each ws In Worksheets
If ws.Name <> "Named Ranges" Then
ws.Cells.Replace What:=strOldName, Replacement:=strNewName, LookAt _
:=xlPart, SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
End If
Next
'Delete old name and replace with the new one
ActiveWorkbook.Names(strOldName).Delete
ActiveWorkbook.Names.Add strNewName, strRefersTo
End If
strOldName = ""
strNewName = ""
i = i + 1
Loop
This is a really simple way to rename a range name. I got this from Paul Kelly at Excel Macro Mastery. Works great.
Sub rename_a_range_name()
Dim NewName As Variant
ThisWorkbook.Names("CurrentName").Name = "NewName"
End Sub
I modified the above code to rename some NAMES. With regards to the code immediately above, to loop through the worksheets and find/replace each NAME in formulas (etc)... I found that I needed to remove the Sheet Reference that is in the beginning of the string for each NAME's name.
'Update all the formulas to use the new name.
myStart = InStr(1, strOldName, "!", vbTextCompare) + 1
myLength = Len(strOldName) - myStart + 1
strOldNameSHORT = Mid(strOldName, myStart, myLength)
myStart = InStr(1, strNewName, "!", vbTextCompare) + 1
myLength = Len(strNewName) - myStart + 1
strNewNameSHORT = Mid(strNewName, myStart, myLength)
For Each ws In Worksheets
ws.Cells.Replace What:=strOldNameSHORT, Replacement:=strNewNameSHORT,
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False,
SearchFormat:=False, ReplaceFormat:=False
Next
Long story short
Putting the update into e.g. the worksheet change event is likely to work.
Root Cause
Update: UDFs called from cells are not allowed to change things on the sheet. (That's what I did)
Old: I guess there are certain calculation processing phases, where updates to names are allowed or not allowed.
More explanation and findings
I wanted to rename a range and it was sometimes ignored and sometimes I got an error 1004 (application- or object-defined error - in German: Anwendungs- oder objektdefinierter Fehler).
Let's say one has a rename function like this:
Function rename( nold As String, nnew As String ) As Boolean
ThisWorkbook.Names(nold).Name = nnew
rename = True
End Function
I found out the following:
if the update is triggered by some UDF (user-defined cell function) on some cell update it will be ignored in some cases and in other cases the 1004 error is raised
e.g. putting some =rename("oldName", "newName") into A1 where oldName exists
why and when it is ignored or the error is raised is unknown to me
if the update is triggered by some event, e.g. the Private Sub Worksheet_Change(ByVal Target As Range) it will always be applied
Other side-effects
In finding out all this and debugging it, it may have caused that cells got locked automagically and thus also causing some 1004 error.
Related
First I should apologize for my very limited VBA coding skills. So the code I have basically does what I want it to do: I have hundreds of Excel files I need to modify at a time repeatedly. If a specific cell ("B1") has the word string "draw" in it, nothing is to happen. If the cell doesn't have the word string "draw", the word "tank" is to be inserted before the word "prep" in the cell. The macro runs through all the files in a given folder, changes the format, outputs to a new folder, etc. This all works beautifully. But on occasion, the cell may contain the word string "pool" instead of "draw". In that case, I don't want to change the cell contents at all. So basically, if "pool" or "draw" is in the cell, do nothing. If they're both not present, add "Tank" before the word string "prep" in the cell. Here's the code I have:
Sub SIS_ALIMS()
Dim wbOpen As Workbook
Dim MyDir As String
MyDir = "C:\Processed data"
strExtension = Dir(MyDir & "\*.xls")
While strExtension <> vbNullString
Set wbOpen = Workbooks.Open(MyDir & "\" & strExtension)
With wbOpen
Set rgFound = Range("B1").Find("draw", MatchCase:=False)
If rgFound Is Nothing Then
Range("B1").replace What:="prep", Replacement:="Tank prep"
Else
End If
Dim SaveName As String
SaveName = ActiveSheet.Range("B8").Text
ActiveWorkbook.SaveAs fileName:="C:\Processed data\ALIMS data\" & _
SaveName & ".txt"
.Close SaveChanges:=False
End With
strExtension = Dir
Wend
Application.ScreenUpdating = True
End Sub
First an observation: Your code does not specify a worksheet in wbOpen, so you may run into problems if a workbook happens not to open on the worksheet you expect. Better to use something like With wbOpen.Sheets(1).
As for your question, instead of using Find you may find it easier to work with the cell value as a string variable:
Dim CellData As String
With wbOpen.Sheets(1)
CellData = .Range("B1").Value
If CellData = "draw" Or CellData = "pool" Then
'do nothing
ElseIf CellData = "prep" Then
.Range("B1").Value = "Tank prep"
Else
'add other conditionals as needed
End If
End With
Finally, if the VBA doesn't need to perform any action when the cell value is "draw" or "pool," then testing for those values is superfluous. The If ... End If block can be replaced with just the conditional that is of interest:
If .Range("B1").Value = "prep" Then .Range("B1").Value = "Tank prep"
I have two Workbooks. I need to take a String from WB1 (I iterate through Column C in WB1, not every cell contains a String, but when a cell contains a string this is the one I want to copy), find it in WB2 and replace it with another String from WB1 (in the same row, but column A). Here is what I have so far:
' Checks if a given File is already open
Public Function FileInUse(sFileName) As Boolean
On Error Resume Next
Open sFileName For Binary Access Read Lock Read As #1
Close #1
FileInUse = IIf(Err.Number > 0, True, False)
On Error GoTo 0
End Function
Sub copyPaste()
Dim destWB As Workbook
Dim destSH As Worksheet
Dim fileName As String
Dim curCell As Range
Dim oldName As Range
Dim result As Range
' turn off screen refresh, recalculate formula to speed up sub
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' For i = 2 To Rows.Count
For i = 2 To 5
fileName = "C:\Users...\" & Workbooks("Ressources calculation.xlsm").Worksheets("Tests costs").Cells(i, 2)
If Not FileInUse(fileName) Then
Set destWB = Workbooks.Open(fileName)
Set destSH = destWB.Sheets("Qualification Matrix")
destSH.Activate
End If
Set curCell = Workbooks("Ressources calculation.xlsm").Sheets("Tests costs").Cells(i, 3)
Set oldName = Workbooks("Ressources calculation.xlsm").Sheets("Tests costs").Cells(i, 1)
If Not IsEmpty(curCell) Then
curCell.Copy
Set result = destWB.Sheets("Qualification Matrix").Cells.Find(What:=oldName.Text, LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True, MatchByte:=True)
If Not result Is Nothing Then
result.PasteSpecial
End If
End If
Next i
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
I have added a MsgBox in the "If Not result" clause which never triggers, so I guess it is not finding the cell. It seems to extract the strings I need to use (in curCell and oldName) fine though (checked also with MsgBox). The cells in which it should search and replace are merged cells, if that makes a difference. I also tried out different values for Cells.Find (leaving all optional parameters, tried all possibilities for lookIn and lookat, MatchByte, tried oldName.Value instead).
This is the first time I'm doing something with Excel Macros/VBA, the last few hours were spend with a lot of trial and error without any result. So I'm sure what I have so far is far from optimal, but I hope that someone can help me with it.
Edit: I narrowed it down a bit. I now activate destSH right before Cells.Find and tried just using a hardcoded example String as a parameter, which works. So I guess the problem is not the find statement but how I try to extract the information I'm looking for with find.
Edit2: As requested, here is a short example walkthrough:
I have a Workbook called "Ressources calculation.xlsm" with three Columns: Current name, File name, New name. Row 4 looks like this:
Misspelledd [File name].xlsx Misspelled
Not every Cell in Column C is filled out. What I'm trying to do is: Iterate through every cell in Column C, if it is not empty copy the string which is in the same row but in Column A, look for it in the file which is noted in Column B and replace it with the right name written under Column C.
Here is a picture of the cell in the destination Workbook which should be found and the text replaced as explained above. It is a merged cell, stretching over rows 2-5.
Edit 3: I finally found out what the problem was. There were "invisible" line breaks at the end of some cells (not really invisible, but you don't easily see them since there are no characters coming after). If this is not the case, the code works.
Try something like this (added some debug.print for troubleshooting)
Sub copyPaste()
Dim destWB As Workbook
Dim destSH As Worksheet
Dim fileName As String
Dim curName, oldName
Dim result As Range
Dim wbRes As Workbook, wsTests As Worksheet
Set wbRes = Workbooks("Ressources calculation.xlsm") 'ThisWorkbook ?
Set wsTests = wbRes.Worksheets("Tests costs")
For i = 2 To 5
fileName = "C:\Users...\" & wsTests.Cells(i, 2)
If Not FileInUse(fileName) Then
Set destWB = Workbooks.Open(fileName)
Set destSH = destWB.Sheets("Qualification Matrix")
curName = Trim(wsTests.Cells(i, 3).Value) '<< always worth adding Trim()...
oldName = Trim(wsTests.Cells(i, 1).Value)
If Len(curName) > 0 Then
Debug.Print "Looking for: '" & oldName & _
"' on sheet '" & destSH.Name & "' in " & _
destWB.FullName
Set result = destSH.UsedRange.Find(What:=oldName, _
LookIn:=xlValues, _
LookAt:=xlWhole)
If Not result Is Nothing Then
Debug.Print "...found"
result.Value = curName
Else
Debug.Print "... not found"
End If
End If
End If 'file not in use
Next i
End Sub
I've been using a function from another StackOverflow question (I'm SO sorry I can't find the original answer!) to help go through a number of cells in Column L that contains a formula that spits our a hyperlinked filepath. It is meant to open each one (workbook), update the values, then save and close the workbook before opening the next one. See below.
Sub List_UpdateAndSave()
Dim lr As Long
Dim i As Integer
Dim WBSsource As Workbook
Dim FileNames As Variant
Dim msg As String
' Update the individual credit models
With ThisWorkbook.Sheets("List")
lr = .Cells(.Rows.Count, "L").End(xlUp).Row
FileNames = .Range("L2:L" & lr).Value
End With
For i = LBound(FileNames, 1) To UBound(FileNames, 1)
On Error Resume Next
If FileNames(i, 1) Like "*.xls*" Then
Set WBSsource = Workbooks.Open(FileNames(i, 1), _
ReadOnly:=False, _
Password:="", _
UpdateLinks:=3)
If Err = 0 Then
With WBSsource
'do stuff here
.Save
.Close True
End With
Else
msg = msg & FileNames(i, 1) & Chr(10)
On Error GoTo 0
End If
End If
Set WBSsource = Nothing
Next i
If Len(msg) > 0 Then
MsgBox "The Following Files Could Not Be Opened" & _
Chr(10) & msg, 48, "Error"
End If
End Sub
The problem now is I am using this to work on a Network drive, and as a result it cause pathing issues with the Connections/Edit Links part. Each of the files are stored on S:\... which as a result of using the Hyperlink formula, won't be able to find the source data. See below the example image of a file that as been opened through a hyperlink cell from my original workbook. When I go to update the Edit Links section of it, it shows these errors.
If I open that lettered drive in Windows Explorer and find the file, it works with no problems. Open, Update Values > Save > Close, it says unknown...
(but if I click Update values here they update correctly.)
If opened using a Hyperlink formula in a cell (Also directing to S:\..) it says it contains links that cannot be updated. I choose to edit links and they're all "Error: Source not found". The location on them also starts off with \\\corp\... and not S:\.
Anyway to fix this? Apologies for the long winded question.
I'm adding this as an answer as it contains code and is a bit long for a comment.
I'm not sure if it's what you're after though.
The code will take the mapped drive and return the network drive, or visa-versa for Excel files. DriveMap is the variable containing the final string - you may want to adapt into a function.
Sub UpdatePath()
Dim oFSO As Object
Dim oDrv As Object
Dim FileName As String
Dim DriveMap As String
Set oFSO = CreateObject("Scripting.FileSystemObject")
FileName = Range("A1")
If InStr(oFSO.GetExtensionName(FileName), "xls") > 0 Then
For Each oDrv In oFSO.drives
If oDrv.sharename <> "" Then
'Changes \\corp\.... to S:\
If InStr(FileName, oDrv.sharename) = 1 Then
DriveMap = Replace(FileName, oDrv.sharename, oDrv.Path)
End If
'Changes S:\ to \\corp\....
' If InStr(FileName, oDrv.Path) = 1 Then
' DriveMap = Replace(FileName, oDrv.Path, oDrv.sharename)
' End If
End If
Next oDrv
End If
End Sub
After hours and days of searching the web in order to find a hint how to fix a bug in my code, I am completely clueless about what might be going on, and am hoping to get some advise from this community.
The code is somewhat complicated, therefore I will not add any snippets, but rather try to explain as simple as I can get it.
I have created a tool (excel macro) that does a lot of analyses on certain data, collected at customer sites using our software (mostly multiple users)
This tool runs well since years, including e.g. filtering to only take into account users that match certain criteria
I want to expand that tool in a way so that it automatically runs multiple times - once for each user.
The way how this works is:
The tool processes data from the first user, and saves the result as a new excel spreadsheet (in which the code continues to run).
the tool processes data from the next user, and again saves the result as a new spreadsheet, and so on.
In the second run the weird behaviour happens: If run in regular mode, the code breaks due to an error; if the code is interrupted by a 'stop' right before the line producing the error and code completion is continued, everything works perfectly fine.
The problem occurs at assignment of a table as range:
Dim rr As Range
Set rr = Workbooks(actWBK).Worksheets(shtName).Range(tableName & "[[#All],[" & header & "]]")
From the second run onwards, the line starting with Set... produces an error (application-defined or object-defined error).
The fact that this assignment works perfectly in the first run made me believe that there is some kind of unspecified assignment to a workbook or the like, but I tested all options and could rule that out;
The really staggering thing is that, as mentioned, when I add a "stop" before, the code works perfectly fine.
I am really out of any ideas, so every answer is more than welcome!
Thank you in advance,
Alexander
I will try to add some code.
The problem occurs in the module sortTable (relevant part after "code here", I always use this kind of "template" to set some standard things):
Sub sortTable(sheetName As String, tableName As String, header As String, dir As XlSortOrder)
' here only logging and error handling settings
'---------------------------------------------------------------------------------------
' code here
'---------------------------------------------------------------------------------------
' deal with #-sign in header
Dim headerParts() As String
headerParts = Split(header, "#")
Dim cleanHeader As String
If UBound(headerParts) = -1 Then
successcode = 2
GoTo errorHandler
ElseIf UBound(headerParts) = 0 Then
cleanHeader = header
Else
cleanHeader = headerParts(0)
Dim i As Integer
For i = 1 To UBound(headerParts)
cleanHeader = cleanHeader & "'#" & headerParts(i)
Next i
End If
' sorting
Dim actWBK As String
actWBK = ActiveWorkbook.name
Dim rr As Range
Set rr = Workbooks(actWBK).Worksheets(sheetName).Range(tableName & "[[#All],[" & cleanHeader & "]]")
ActiveWorkbook.Worksheets(sheetName).ListObjects(tableName).Sort.SortFields _
.Clear
ActiveWorkbook.Worksheets(sheetName).ListObjects(tableName).Sort.SortFields _
.Add key:=Range(tableName & "[[#All],[" & cleanHeader & "]]"), SortOn:=xlSortOnValues, Order _
:=dir, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets(sheetName).ListObjects(tableName).Sort
.header = xlYes
.MatchCase = True
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
'---------------------------------------------------------------------------------------
' sub cleanup on exit; don't make changes below this line
'---------------------------------------------------------------------------------------
' here only logging and error handling
End Sub
The procedure is called from a different module called QuickSort which takes as argument an array:
Public Sub QuickSort(vArray As Variant)
' here only logging and error handling
Dim wsName As String
wsName = "tempSort"
Application.DisplayAlerts = False
On Error Resume Next
Sheets(wsName).Delete
On Error GoTo errorHandler
Application.DisplayAlerts = True
Worksheets.Add After:=Sheets(Sheets.count)
ActiveSheet.name = wsName
Cells(1, 1) = "Header"
Dim rr As Range
Set rr = Range(Cells(2, 1), Cells(UBound(vArray) + 2 - LBound(vArray), 1))
Set rr = rr.Resize(UBound(vArray) + 1 - LBound(vArray), 1)
rr.value = myTransposeArray(vArray)
Set rr = Nothing
ActiveSheet.ListObjects.Add( _
xlSrcRange, _
Range(Cells(1, 1), Cells(UBound(vArray) + 2 - LBound(vArray), 1)), _
, xlYes).name = "tempSortTable"
sortTable sheetName:=wsName, tableName:="tempSortTable", header:="Header", dir:=xlAscending
' more code hereafter
Try changing this
Dim actWBK As String
actWBK = ActiveWorkbook.name
Dim rr As Range
Set rr = Workbooks(actWBK).Worksheets(sheetName).Range(tableName & "[[#All],[" & cleanHeader & "]]")
To
Dim actWbk as workbook
Set actwbk = activeworkbook
dim ws as worksheet
set ws = actwbk.worksheets(sheetname)
dim s as string
s = tableName & "[[#All],[" & cleanHeader & "]]"
Dim rr as range
Set rr = ws.range(s)
Then when it breaks you can inspect each in turn to see if they point to what you think they should
A collegue of mine found a workaround to overcome the problem (all credits onto you, Thomas!):
instead of referencing the range as
Set rr = Workbooks(actWBK).Worksheets(sheetName).Range(tableName & "[[#All],[" & cleanHeader & "]]")
I changed the code to
Set rr = Workbooks(actWBK).Worksheets(sheetName).ListObjects(tableName).ListColumns(cleanHeader).Range
By that, everything works perfectly fine, no matter if in the first run or in subsequent runs.
There is also a suspicion what might cause the problem (which clearly is not resolved itself, this solution is just a workaround!), and it has to do with something going on during saving the workbook using SaveAs.
I am not clear at all what could be this reason, but for those who face a similar problem, I'd like to explain what I did in my code:
By opening a file containing the tool
AnalysisTool.xlsm
the macro starts running. In order to get data, the code opens an xml file as excel table; this table temporarily is called something like
Book1.xlsx
The code copies the data from Book1 to AnalysisTool; in order to remain the tool unchanged, the file is saved as something like
AnalysisResult_20180222_01.xlsm <- this is the file in which code is executed!
Book1 is closed without saving.
When the analysis is finished, the workbook is saved without close.
Upon re-run,
All result tabs in AnalysisResult_20180222_01.xlsm are deleted, a new xml data file is opened, data is copied, and the code-bearing file is saved as
AnalysisResult_20180222_02.xlsm <- this is the file in which code now is executed!
etc.
As I said, I am not sure what goes wrong, but by changing this one line as described makes everything work perfectly fine.
Hope this might be helpful to anybody!
I am trying to copy the string values(column titles) from another workbook in row 4 as captions for checkboxes in the workbook where I am running the code. This is what I have so far and it is not working because it is showing the error message "Subscript out of range, run time error 9" Here is what I have. After the error message pops up the line marked below is highlighted. Can anybody help me please. Thank you very much.
Function CallFunction(SheetName As Variant) As Long
Dim text As String
Dim titles(200) As String ' Dim titles(200) As String ' Array
Dim nTitles As Integer
Dim wks As Worksheet
Dim myCaption As String
PathName = Range("F22").Value
Filename = Range("F23").Value
TabName = Range("F24").Value
ControlFile = ActiveWorkbook.Name
Workbooks.Open Filename:=PathName & "\" & Filename
ActiveSheet.Name = TabName
Set wks = Workbooks("Filename").Worksheets(SheetName).Activate ' <= Highlights this line ****
For i = 1 To 199
If Trim(wks.Cells(4, i).Value) = "" Then
nTitles = i - 1
Exit For
End If
titles(i - 1) = wks.Cells(4, i).Value
Next
i = 1
For Each cell In Range(Sheets("Sheet1").Cells(4, 1), Sheets("Sheet1").Cells(4, 1 + nTitles))
myCaption = Sheets("Sheet1").Cells(4, i).Value
With Sheets("Sheet1").checkBoxes.Add(cell.Left, _
cell.Top, cell.Width, cell.Height)
.Interior.ColorIndex = 12
.Caption = myCaption
.Characters.text = myCaption
.Border.Weight = xlThin
.Name = myCaption
End With
i = i + 1
Next
End Function
Subscript out-of-range typically indicates that a specified Worksheet does not exist in the workbooks Worksheets collection.
Otherwise, are you sure that the workbook specified by FileName is already open? If not, that will raise the same error.
Ensure that A) the file is already open (or use the Workbooks.Open method to open it), and B) ensure that such a worksheet already exists (if not, you will need to create it before you can reference it by name).
Update
You have Workbooks("FileName") where "Filename" is a string literal. Try changing it to simply Filename (without the quotation marks) (this seems like the OBVIOUS error).
Also worth checking:
I also observe this line:
ActiveSheet.Name = TabName
If the sheet named by SheetName is active when the workbook opens, then that line will effectively rename it, so you will not be able to refer to it by SheetName, but instead you would have to refer to it by Worksheets(TabName). ALternatively, flip the two lines so that you activate prior to renaming:
Set wks = Workbooks(Filename).Worksheets(SheetName).Activate
ActiveSheet.Name = TabName
For further reading: avoid using Activate/Select methods, they are confusing and make your code harder to interpret and maintain:
How to avoid using Select in Excel VBA macros
If that is the case, then you could do simply:
Workbooks(Filename).Worksheets(SheetName).Name = TabName