I am coping one worksheet from all the workbooks in a folder and pasting it to a new workbook called workbook2. Issue I am facing is, the VBA code I am using is not performing Data->Edit Links->Break Link action.
Because of this, workbook2 throws the warning, "This workbook contains links to one or more external sources that could be unsafe." every time the workbook2 is opened.
After copy pasting all the worksheets, the code I am using to break link before saving and closing the workbook2 is,
On Error Resume Next
ExternalLinks = workbook2 .LinkSources(Type:=xlLinkTypeExcelLinks)
If IsArray(ExternalLinks) Then
For breaklink = LBound(ExternalLinks) To UBound(ExternalLinks)
wb1.breaklink Name:=ExternalLinks(breaklink), Type:=xlLinkTypeExcelLinks
Next breaklink
End If
On Error GoTo 0
Try using the next adapted code. It should send a message when a specific link cannot be removed:
Sub testBreakLinks()
Dim ExternalLinks, brLink As Long, WB1 As Workbook
Set WB1 = ThisWorkbook 'use here your workbook to be processed
ExternalLinks = WB1.LinkSources(Type:=xlLinkTypeExcelLinks)
If IsArray(ExternalLinks) Then
For brLink = LBound(ExternalLinks) To UBound(ExternalLinks)
On Error Resume Next
WB1.BreakLink name:=ExternalLinks(brLink), Type:=xlLinkTypeExcelLinks
If err.Number <> 0 Then
MsgBox err.Description & " - " & ExternalLinks(brLink)
err.Clear: On Error GoTo 0
End If
Next brLink
End If
On Error GoTo 0
End Sub
It will work, except the cases of protected sheets where the external links cannot be broken and an error is not raised, neither...
Edited:
I created a procedure and a function able to return addresses of all cells containing such links and a list of protected sheets, where the external links cannot be found/broken:
Sub testFindLinkCellAddresses()
Dim arrLnk, ExternalLinks, lnk As Variant, wb As Workbook
Set wb = ThisWorkbook
ExternalLinks = wb.LinkSources(Type:=xlLinkTypeExcelLinks)
For Each lnk In ExternalLinks
arrLnk = ExtLinkCells(CStr(lnk), wb)
If arrLnk(0)(0) <> "" Then
Debug.Print "External links for " & lnk & " exist in cells:" & vbCrLf & Join(arrLnk(0), "|")
Debug.Print "____________________________"
Else
Debug.Print "No external links found for " & lnk & vbCrLf & _
IIf(arrLnk(1)(0) <> "", "But the next sheets are protected:" & vbCrLf & _
Join(arrLnk(1), ", ") & ", " & vbCrLf & " and links cannot be found/broken even if they exist there!", "")
Debug.Print "____________________________"
End If
Next
End Sub
Function ExtLinkCells(strLnk As String, wb As Workbook) As Variant
Dim sh As Worksheet, rngForm As Range, strName As String
Dim arr, arrPr, k As Long, p As Long, cel As Range
strName = Right(strLnk, Len(strLnk) - InStrRev(strLnk, "\"))
strName = "[" & strName & "]"
ReDim arr(1000)
ReDim arrPr(wb.Sheets.count)
For Each sh In wb.Sheets
If sh.ProtectContents Then arrPr(p) = sh.name: p = p + 1
On Error Resume Next
Set rngForm = sh.UsedRange.SpecialCells(xlCellTypeFormulas)
On Error GoTo 0
If Not rngForm Is Nothing Then
For Each cel In rngForm.cells
If InStr(cel.Formula, strName) > 0 Then
arr(k) = Split(cel.Address(external:=True), "]")(1): k = k + 1
End If
Next
End If
Next
If k > 0 Then ReDim Preserve arr(k - 1) Else ReDim arr(0)
If p > 0 Then ReDim Preserve arrPr(p - 1) Else ReDim arrPr(0)
ExtLinkCells = Array(arr, arrPr)
End Function
The code can be improved, of course. For instance, the array keeping the protected sheets array should be declared as Private on top of the module and skip their processing part if the array is not empty. Showing the array content only once, at the end, if the case... But I do not need such a code. I just tried putting myself in the OP's skin and finding a way to better clarify the issue. Knowing the password, the protected sheets can be previously unprotected and protected again at the end (in code, of course) in the sequence trying to break them...
Related
I'm running a code which basically loops through every sheet of the workbook (containing a huge number of sheets) and copies the text contained in some cells identified through the name manager. Basically, in every sheet there are 4 cells identified through the name manager as "Item_1","Item_2","Item_3","Item_4", as shown in the example.
This is a version of the code
for each anyWS in thisworkbook.worksheets
name=anyWS.Name
num_row=2
for num_row=2 To 4
if workbooks(xx).sheets(name).range("Item_"& num_row).value <> "" then
'the code does something...
else
end if
next
next
In some sheets, though, I have also a fifth item, so that I defined it "Item_5" through the name manager for that sheet. I edited this part of the above code: for num_row=2 To 4 into for num_row=2 To 5, but it fails whenever a sheet doesn't have "Item_5" defined through the name manager, and the code displays an error: the object workbooks(xx).sheets(name).range("Item_"& num_row).value does not exist when num_row=5.
I tried the following:
define automatically "Item_5" through name manager for every sheet in the wb, but it doesn't work.
insert a statement On error goto where the code fails, but unsuccessfully, since when the loop is executed is "stuck" in the error. The code shouldn't stop. Simply, when it doesn't find the range named "Item_5", it should exit the for loop and go to the following sheet.
add a condition like If not (workbooks(xx).sheets(name).range("Item_"& num_row).value) is nothing then, to identify the cases when that name doesn't exist, but it doesn't work.
The third option seems the better to me, so I ask you if there is a way to identify the non-existing object defined through the name manager, in order to prevent that error.
Loop Through Named Cell Ranges
Option Explicit
Sub LoopWorksheets()
Dim ws As Worksheet
Dim Cell As Range
Dim n As Long
For Each ws In ThisWorkbook.Worksheets
For n = 1 To 5
On Error Resume Next
Set Cell = ws.Range("Item_" & n)
On Error GoTo 0
If Not Cell Is Nothing Then ' named cell exists
If Len(CStr(Cell.Value)) > 0 Then ' named cell is not blank
'the code does something...
Debug.Print "The value of cell '" & Cell.Address(0, 0) _
& "' ('Item_" & n & "') in worksheet '" _
& ws.Name & "' is equal to '" & CStr(Cell.Value) & "'."
Else ' named cell is blank
Debug.Print "The cell '" & Cell.Address(0, 0) _
& "' ('Item_" & n & "') in worksheet '" _
& ws.Name & "' is blank."
End If
Set Cell = Nothing
Else ' named cell doesn't exist
Debug.Print "The named cell '" & "Item_" & n _
& "' was not found in worksheet '" & ws.Name & "'."
End If
Next n
Next ws
MsgBox "Looped through worksheets.", vbInformation
End Sub
Edit
You could use a function and do something like the following (simplified).
Sub LoopWorksheetsUsingFunction()
Dim ws As Worksheet
Dim n As Long
Dim RangeName As String
For Each ws In ThisWorkbook.Worksheets
For n = 1 To 5
RangeName = "Item_" & n
If NamedRangeExists(ws, RangeName) Then
If ws.Range(RangeName).Value <> "" Then
' do something
End If
End If
Next n
Next ws
End Sub
Function NamedRangeExists( _
ByVal ws As Worksheet, _
ByVal RangeName As String) _
As Boolean
Dim rg As Range
On Error Resume Next
Set rg = ws.Range(RangeName)
On Error GoTo 0
NamedRangeExists = Not rg Is Nothing
End Function
Solution Template SetUp
Been scratching around for the last 5 days here and across the net to find something that works for multiple files. Many a late night/early hours of the morning unsuccessfully piecing together/coding to get a result. Thanks in advance.
The following code is from get-digital-help.com/copyrename-a-file-excel-vba written by Oscar
It works for 1 file, Ive got 8,000 files to do across a deep folder structure so but I'd really like each row to look at a source path, source file name, destination path and destination file:
For each row:
Column A list the source path
Column B lists the source file name
Column C lists to destination path
Column D lists the new file name
Column E writes "Success" or "Fail" validation.
if file name already exists in destination, then "Fail"
If source file doesn't exist, then "Fail"
Nice to have/completely optional!!! :)
Check if source file column A&B exists, = True or False in column F. Where True, then proceed with copy and rename.
If destination file already exist, the fail and column F = duplicate
Leave first row to put in column header names.
Sub CopyRenameFile()
'Dimension variables and declare data types
Dim src As String, dst As String, fl As String
Dim rfl As String
'Save source directory specified in cell A2 to variable src
src = Range("A2")
'Save destination directory specified in cell C2 to variable dst
dst = Range("C2")
'Save file name specified in cell B2 to variable fl
fl = Range("B2")
'Save new file name specified in cell D2 to variable rfl
rfl = Range("D2")
'Enable error handling
On Error Resume Next
'Copy file based on variables src and fl to destination folder based on variable dst and name file based on value in rfl
FileCopy src & "\" & fl, dst & "\" & rfl
'Check if an error has occurred
If Err.Number <> 0 Then
'Show error using message box
MsgBox "Copy error: " & src & "\" & rfl
End If
'Disable error handling
On Error GoTo 0
End Sub
Copy Files Using a File List
This solution consists of three procedures. You run only the first: copyRenameFile. The other two, getOffsetColumn and writeOffsetRange are being called by the first, when necessary.
It is best tested with a new workbook. Insert a module and copy the code into it. Now open your original workbook and copy certain values to e.g. Sheet1 of the new workbook. Since the code is written for Thisworkbook (the workbook containing this code), the original workbook will be safe (will not be written to).
First adjust the values in the constants sections (titled Worksheet and Other). Then test the empty worksheet. Then test with one folder in column A then with more and slowly continue testing with other columns. Possible errors should be suppressed and their messages (descriptions) should appear in VBE's Immediate window (CTRL+G).
As a byproduct of this investigation, I've also added the createFolders function to create folders in one case when MkDir 'cannot', and two procedures to test it.
The Code
Option Explicit
Sub copyRenameFile()
' Initialize error handling.
Const ProcName As String = "copyRenameFile"
On Error GoTo clearError ' Turn on error trapping.
' Worksheet
Const wsName As String = "Sheet1" ' Worksheet Name
Const FirstRow As Long = 2 ' First Row Number
Const LastRowCol As Variant = "A" ' Last Row Column Index
Dim srcCols As Variant ' Source Columns Array
srcCols = VBA.Array("A", "B", "C", "D")
Dim tgtCols As Variant ' Target Columns Array
tgtCols = VBA.Array("E", "F")
' Other
Dim filMsg() As Variant ' File Messages
filMsg = VBA.Array("Fail", "Success")
Dim folMsg() As Variant ' Folder Messages
folMsg = VBA.Array(False, True, "Duplicate")
Dim PathDelimiter As String
PathDelimiter = Application.PathSeparator
Dim wb As Workbook
Set wb = ThisWorkbook ' 'Thisworkbook' is the workbook containing this code.
' Define Last Row Column Range ('rng').
Dim ws As Worksheet
Set ws = wb.Worksheets(wsName)
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, LastRowCol).End(xlUp).Row
If LastRow < FirstRow Then
GoTo FirstRowBelowLastRow
End If
Dim rng As Range
Set rng = ws.Range(ws.Cells(FirstRow, LastRowCol), _
ws.Cells(LastRow, LastRowCol))
' Write Source Column Ranges to Source Jagged Array ('Source').
Dim ubcS As Long
ubcS = UBound(srcCols)
Dim Source As Variant
ReDim Source(0 To ubcS)
Dim Data As Variant
Dim j As Long
For j = 0 To ubcS
getOffsetColumn Data, srcCols(j), rng
Source(j) = Data
Next j
' Define Target Jagged Array ('Target').
Dim ubcT As Long
ubcT = UBound(tgtCols)
Dim ubs As Long
ubs = UBound(Source(0))
Dim Target As Variant
ReDim Target(0 To ubcT)
ReDim Data(1 To ubs, 1 To 1)
For j = 0 To ubcT
Target(j) = Data
Next j
' Declare additional variables for the For Next loop.
Dim i As Long
Dim Copied As Long
Dim srcPath As String
Dim tgtPath As String
' Loop through rows of arrays of Source Jagged Array, check folders,
' check files and finally copy if condition is met. At the same time
' write results to arrays of Target Jagged Array.
' The condition to copy is met when source file exists,
' and target file does not.
For i = 1 To ubs
' Folders
srcPath = Source(0)(i, 1)
If Dir(srcPath, vbDirectory) = "" Then
' Source Folder and Source File do not exist.
Target(0)(i, 1) = filMsg(0)
Target(1)(i, 1) = folMsg(0)
GoTo NextRow
End If
' Source Folder exists.
tgtPath = Source(1)(i, 1)
If Dir(tgtPath, vbDirectory) = "" Then
' Target Folder and Target File do not exist.
Target(0)(i, 1) = filMsg(0)
Target(1)(i, 1) = folMsg(0)
GoTo NextRow
End If
' Source Folder and Target Folder exist.
' Files
srcPath = srcPath & PathDelimiter & Source(2)(i, 1)
If Dir(srcPath) = "" Then
' Source File does not exist.
Target(0)(i, 1) = filMsg(0)
Target(1)(i, 1) = folMsg(0)
GoTo NextRow
End If
' Source File exists.
tgtPath = tgtPath & PathDelimiter & Source(3)(i, 1)
If Dir(tgtPath) <> "" Then
' Target File exists.
Target(0)(i, 1) = filMsg(0)
Target(1)(i, 1) = folMsg(2)
GoTo NextRow
End If
' Source File exists and Target File does not.
Target(0)(i, 1) = filMsg(1)
Target(1)(i, 1) = folMsg(1)
' Copy
FileCopy srcPath, tgtPath
' Count files copied.
Copied = Copied + 1
NextRow:
Next i
' Write values (results) from arrays of Target Jagged Array
' to Target Columns.
For j = 0 To ubcT
writeOffsetRange Target(j), tgtCols(j), rng
Next j
' Inform user.
MsgBox "Copied " & Copied & " files.", vbInformation, "Success"
ProcExit:
Exit Sub
FirstRowBelowLastRow:
Debug.Print "'" & ProcName & "': First row below last row."
GoTo ProcExit
clearError:
Debug.Print "'" & ProcName & "': " & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
On Error GoTo 0 ' Turn off error trapping.
GoTo ProcExit
End Sub
Sub getOffsetColumn(ByRef Data As Variant, _
OffsetColumnIndex As Variant, _
ColumnRange As Range)
' Initialize error handling.
Const ProcName As String = "getOffsetColumn"
On Error GoTo clearError ' Turn on error trapping.
Data = Empty
If ColumnRange Is Nothing Then
GoTo NoRange
End If
Dim ws As Worksheet
Set ws = ColumnRange.Worksheet
If ColumnRange.Rows.Count > 1 Then
Data = ColumnRange.Offset(, ws.Columns(OffsetColumnIndex).Column _
- ColumnRange.Column) _
.Value
Else
ReDim Data(1 To 1, 1 To 1)
Data(1, 1) = ColumnRange.Offset(, ws.Columns(OffsetColumnIndex) _
.Column _
- ColumnRange.Column) _
.Value
End If
ProcExit:
Exit Sub
NoRange:
Debug.Print "'" & ProcName & "': No Range."
GoTo ProcExit
clearError:
Debug.Print "'" & ProcName & "': " & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
On Error GoTo 0 ' Turn off error trapping.
GoTo ProcExit
End Sub
Sub writeOffsetRange(Data As Variant, _
OffsetColumnIndex As Variant, _
ColumnRange As Range)
' Initialize error handling.
Const ProcName As String = "writeOffsetColumn"
On Error GoTo clearError ' Turn on error trapping.
If ColumnRange Is Nothing Then
GoTo NoRange
End If
Dim ws As Worksheet
Set ws = ColumnRange.Worksheet
ColumnRange.Offset(, ws.Columns(OffsetColumnIndex).Column _
- ColumnRange.Column).Value = Data
ProcExit:
Exit Sub
NoRange:
Debug.Print "'" & ProcName & "': No Range."
GoTo ProcExit
clearError:
Debug.Print "'" & ProcName & "': " & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
On Error GoTo 0 ' Turn off error trapping.
GoTo ProcExit
End Sub
The Byproduct
' e.g. "C:\Test" is an existing folder, "C:\Test\Test1" is not.
' When you want to create the folder "C:\Test\Test1\Test2", 'MkDir' will return
' "Run-time error '76': Path Not found", because "C:\Test\Test1" does not exist.
' The 'createFolders' function remedies this by creating as many folders
' as needed. In the previous example it first creates "C:\Test\Test1" and
' only then creates "C:\Test\Test1\Test2" in it.
' The function returns 'True' if the folder previously existed or now exists.
' The function returns 'False' if 'PathString' is invalid.
Function createFolders(PathString As String) As Boolean
' Initialize error handling.
Const ProcName As String = "createFolders"
On Error GoTo clearError ' Turn on error trapping.
' Split Path String ('PathString') by System Path Separator ('Delimiter')
' into 1D zero-based Folders Array 'Folders()'.
Dim Delimiter As String
Delimiter = Application.PathSeparator
Dim Folders() As String
Folders = Split(PathString, Delimiter)
' Define Last Subscript ('LastSS') to be considered, because Path String
' could be ending with a System Path Separator.
Dim LastSS As Long
LastSS = UBound(Folders)
If Folders(LastSS) = "" Then
LastSS = LastSS - 1
End If
' Using Folders Array, write paths to Paths Array ('Paths()').
Dim Paths() As String
ReDim Paths(0 To LastSS)
Paths(0) = Folders(0)
Dim j As Long
If LastSS > 0 Then
For j = 1 To LastSS
Paths(j) = Paths(j - 1) & Delimiter & Folders(j)
Next j
End If
' Create each folder if it does not exist.
For j = 0 To LastSS
If Dir(Paths(j), vbDirectory) = "" Then
MkDir Paths(j)
End If
Next j
' Write result.
createFolders = True
ProcExit:
Exit Function
clearError:
Debug.Print "'" & ProcName & "': " & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
On Error GoTo 0 ' Turn off error trapping.
GoTo ProcExit
End Function
Sub testCreateFolders()
Const PathString As String = "C:\Test\Test1\Test2"
Dim Result As Boolean
Result = createFolders(PathString)
If Result Then
MsgBox "If the path previously didn't exist, now it certainly does."
Else
MsgBox "The supplied path is invalid."
End If
End Sub
Sub testMkDir()
Const PathString As String = "C:\Test\Test1\Test2"
MkDir PathString
End Sub
I have an Excel work book which is acting as a database and a UserForm which acts as a UI. Both are in different workbooks.
I want to populate the UserForm with data from Excel workbook .
Private Sub CommandButton4_Click()
Dim n As Long, i As Long
n = 0
Dim mydata1 As Workbook
Set mydata1 = Workbooks.Open("\\NTSYDFSP150\Shared\fmd\credit\LEM_Reports\SV References\SV Entry Form Input.xlsx")
mydata1.Worksheets("sheet1").Activate
mydata1.Worksheets("sheet1").Range("A1").Select
n = Worksheets("sheet1").Range("a1").CurrentRegion.Rows.Count
For i = 2 To n
If Trim(Sheet1.Cells(i, 1)) <> Trim(UserForm1.TextBox157.Text) And i = n Then
MsgBox ("Name not found")
End If
If Trim(Sheet1.Cells(i, 1)) = Trim(UserForm1.TextBox157.Text) Then
UserForm1.TextBox1.Text = Sheet1.Cells(i, 1)
Exit For
End If
Next i
mydata1.Save
mydata1.Close
MsgBox "Data searched successfully", 0, vbNullString
End Sub
Issue :
When I run the code am not able to retrieve data from workbook Excel database.
Sheet1.Cells(i, 1): - This field still refers to Shee1 from User form work book while it should be referring to work book at shared drive location since I had activated and opened that .
Note: n is calculated correctly.
I cleaned up your code and qualified the ranges where necessary. Not qualifying the ranges is most likely the error here. Example: Worksheets("sheet1").Range("a1"). ... needs to be mydata1.Worksheets("sheet1").Range("a1"). .... Try the following code:
Private Sub CommandButton4_Click()
Dim n As Long, i As Long
n = 0
Dim mydata1 As Workbook
Set mydata1 = Workbooks.Open("\\NTSYDFSP150\Shared\fmd\credit\LEM_Reports\SV References\SV Entry Form Input.xlsx")
n = mydata1.Worksheets("sheet1").Range("a1").CurrentRegion.Rows.Count
For i = 2 To n
If Trim(mydata1.Sheet1.Cells(i, 1)) <> Trim(UserForm1.TextBox157.Text) And i = n Then
MsgBox ("Name not found")
End If
If Trim(mydata1.Sheet1.Cells(i, 1)) = Trim(UserForm1.TextBox157.Text) Then
UserForm1.TextBox1.Text = mydata1.Sheet1.Cells(i, 1)
Exit For
End If
Next i
mydata1.Save
mydata1.Close
MsgBox "Data searched successfully", 0, vbNullString
End Sub
Note that activating the workbook and .Selecting a Range is not necessary in this case (so I deleted it) and should be avoided in general (see comment above for additional advice).
This is just a suggested way to prevent opening another workbook:
Private Sub CommandButton4_Click()
Dim wbPath As String: wbPath = "\\NTSYDFSP150\Shared\fmd\credit\LEM_Reports\SV References\"
Dim wbName As String: wbName = "SV Entry Form Input.xlsx"
Dim wsName As String: wsName = "sheet1"
Dim arrList As Object: Set arrList = CreateObject("System.Collections.ArrayList")
Dim lr As Long, x As Long
'Get the last row from A column, notice we need R1C1 notation for Excel4Macro
lr = ExecuteExcel4Macro("MATCH(""zzz"",'" & wbPath & "[" & wbName & "]" & wsName & "'!C1)")
'Let's use an ArrayList to get our validation list
For x = 2 To lr
arrList.Add Trim(ExecuteExcel4Macro("'" & wbPath & "[" & wbName & "]" & wsName & "'!R" & x & "C1"))
Next x
'Check if ArrayList contains your lookup value
If arrList.Contains(Trim(UserForm1.TextBox157.Text)) Then
UserForm1.TextBox1.Text = UserForm1.TextBox157.Text
Else
MsgBox ("Name not found")
End If
MsgBox "Data searched successfully"
End Sub
The code below is what i use to rename a bunch of sheets within a workbook. it works perfectly. It renames the sheet based off of a cell in that sheet. but now i have two sheets trying to use the same name. So i want to keep the same code but add a loop so if that happens, it will add a "2" to the second sheet. Ie cell contains "John Doe". Sheet will rename to "John Doe" and the next sheet that tries to use it will rename "John Doe 2"
Thank you
Sub RenameLaborLog()
Dim rs As Worksheet
For Each rs In Sheets
rs.Name = Split(rs.Range("H4").Value, " ")(1) & ", " & Left(Split(rs.Range("H4").Value)(0), 1) & "."
Next rs
End Sub
just to show another way you can reach your goal
Sub RenameLaborLog()
Dim rs As Worksheet, i As Long, str As String
On Error Resume Next
For Each rs In Sheets
str = Split(rs.Range("H4").Value, " ")(1) & ", " & Left(Split(rs.Range("H4").Value)(0), 1) & "."
rs.Name = str
i = 1
While Err.Number <> 0 And i < 20
Err.Clear: i = i + 1
rs.Name = str & i
Wend
If Err.Number <> 0 Then MsgBox "Error: " & rs.Name & " cant be set to any " & str: Exit Sub
Next rs
End Sub
it tries to set the name (and if that is not working it sets the name & 2 - 19 (if that doesnt work, it pops up a message box and exits the sub)
Use a controlled error to adjust the string containing the worksheet name until it find something it can use.
Sub RenameLaborLog()
Dim rs As Worksheet, snam As String, idupe As Long
On Error GoTo bm_Dupe_WS_Name
For Each rs In Sheets
idupe = 1
snam = Split(rs.Range("H4").Value, " ")(1) & ", " & _
Left(Split(rs.Range("H4").Value)(0), 1) & "."
rs.Name = snam
Next rs
bm_Dupe_WS_Name:
If idupe > 8 Then
Debug.Print Err.Number & ": " & snam & " - " & Err.Description
Exit Sub
ElseIf Right(snam, 1) = CStr(idupe) Then
snam = Trim(Left(snam, Len(snam) - 1))
End If
idupe = idupe + 1
snam = snam & Chr(32) & idupe
Resume
End Sub
I have it set yo attempt a numerical suffix up to 9. It it reaches that, it reports the error and exits the sub. I would not recommend running this without an escape clause. If nothing else, you may run into an illegal character when parsing the string for the worksheet name.
Based on the link #Scott Craner provided in his comment, I am providing another solution that I believe is a bit more functional and cleaner and easier to read.
Sub RenameLaborLog()
Dim rs As Worksheet, sName As String
For Each rs In Sheets
sName = Split(rs.Range("H4").Value, " ")(1) & ", " & Left(Split(rs.Range("H4").Value)(0), 1) & "."
i = 1
Do
If Not WorksheetExist(sName) Then
rs.Name = sName
Exit Do
Else: sName = sName & "_" & i + 1
End If
Loop
Next rs
End Sub
Function WorksheetExist(sName As String, Optional wb As Workbook) As Boolean
Dim wbCheck As Workbook, ws As Worksheet
If wb Is Nothing Then Set wbCheck = ThisWorkbook Else: Set wbCheck = wb
WorksheetExist = False
For Each ws In wbCheck.Worksheets
If ws.Name = sName Then
WorksheetExist = True
Exit For
End If
Next
End Function
Jeeped beat me to it, but here is another possible adjustment you could make:
Sub RenameLaborLog()
Dim rs As Worksheet, wsName As String, wsCheck As Worksheet, i As Integer
For Each rs In Sheets
' Get the sheet name
wsName = Split(rs.Range("H4").Value, " ")(1) & ", " & Left(Split(rs.Range("H4").Value)(0), 1) & "."
' Check if it exists
Set wsCheck = Nothing: On Error Resume Next: Set wsCheck = wsName: On Error GoTo 0
' Check if multiples already exist
While Not wsCheck Is Nothing
' If even one exits, "i" will be iterated
i = i + 1
Set wsCheck = Nothing: On Error Resume Next: Set wsCheck = wsName & "_" & i: On Error GoTo 0
Wend
' If at least one name already existed, name it with the current iteration.
If Not i = 0 Then wsName = wsName & "_" & i
rs.Name = wsName
Next rs
Set rs = Nothing: Set wsCheck = Nothing
End Sub
I am trying to reassign all the linked cells for checkboxes on three given worksheets in a large collection of workbooks.
The macro I have works successfully on any book I have open:
Sub CheckBoxesControl()
On Error Resume Next
Dim i As Long
For i = 1 To 400
Sheet4.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AA" & i
Sheet21.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AB" & i
Sheet22.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AC" & i
Next i
End Sub
However I want to run this across a large number of sheets, so I tried the following:
Sub CheckBoxesControl()
On Error Resume Next
Dim path As String
Dim file As String
Dim wkbk As Workbook
Dim i As Long
Application.ScreenUpdating = False
Application.DisplayAlerts = False
path = "C:\file\path\"
file = Dir(path)
Do While Not file = ""
Workbooks.Open (path & file)
Set wkbk = ActiveWorkbook
For i = 1 To 400
Sheet4.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AA" & i
Sheet21.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AB" & i
Sheet22.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AC" & i
Next i
wkbk.Save
wkbk.Close
file = Dir
Loop
End Sub
The macro certainly opens and closes each file, and runs without error, but it is not having the desired affect.
It only changes the check boxes for the sheet I run the macro from still (despite apparently opening saving and closing all the others).
Am I failing to correctly set the active workbook?
EDIT 1: Suggested fix (failed)
Method suggested in comments (proved unsuccessful):
Sub CheckBoxesControl()
On Error Resume Next
Dim path As String
Dim file As String
Dim wkbk As Workbook
Dim i As Long
Application.ScreenUpdating = False
Application.DisplayAlerts = False
path = "C:\file\path\"
file = Dir(path)
Do While Not file = ""
Set wkbk = Workbooks.Open(path & file)
For i = 1 To 400
wkbk.Sheet4.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AA" & i
wkbk.Sheet21.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AB" & i
wkbk.Sheet22.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AC" & i
If Err.Number <> 0 Then
End If
Next i
wkbk.Save
wkbk.Close
file = Dir
Loop
End Sub
EDIT 2: REMOVING ON ERROR RESUME NEXT
Suggestedion to remove the error ignoring has illustrated the following: when the macro runs an error:
Run-time error 1004
The item with the specific name wasn't found.
Debugging this error highlights:
Sheet4.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AA" & i
I believe I realise what this issue is: I'm using a "go between 1 and 400" loop to ensure I catch all the checkboxes on each page, but there isn't a checkbox for each one of those instances, (checkbox1 doesn't exist for example, on all pages - notably not on sheet 4)
I remember now this is why I had On error resume next there in the first place... but I need "next" to be the next "i" in the loop, not the next expression completely.
Update 4
For those keeping score at home, the problem is that OP was using the sheets CodeName, which cannot be used when referring to it from a macro in another spreadsheet.
Modify to accept the worksheet Name, and either of the subs can be called like:
Dim ws As Worksheet
Set ws = wkbk.Sheets("10. Prevention Finance")
UpdateChkBoxes3 ws, "ChkBoxOutput!AA"
Set ws = wkbk.Sheets("...") '#Modify the sheet name
UpdateChkBoxes3 ws, "ChkBoxOutput!AB"
Set ws = wkbk.Sheets("...") '#Modify the sheet name
UpdateChkBoxes3 ws, "ChkBoxOutput!AC"
Update 3 (non-ActiveX Checkboxes)
Sub UpdateChkBoxes3(sht as Worksheet, lnkdCell as String)
Dim cb as CheckBox
Dim cbNum As Integer
With sht
For Each cb In sht.CheckBoxes
cbNum = Replace(cb.Name, "Check Box ", vbNullString)
cb.LinkedCell = lnkdCell & cbNum
Next
End With
I also revised the sub in Update 2, previously had pasted in my testing code, instead of the proper sub that requires sht/lnkdCell as arguments.
Update 2
To account for non-indexed checkbox names, but still looping over all checkboxes in each worksheet, call this subroutine. I attempt to get the numeric value from the checkbox's .Name property, this should relate it to the cell location just like your i indexing did before, only you will avoid errors where checkboxes don't exist, because we're not looping over an Index, we're looping over the shapes themselves. This should work with ActiveX checkboxes:
Sub UpdateChkBoxes2(sht As Worksheet, lnkdCell As String)
'To address non-sequential/missing check box names not aligned with index
Dim cb As OLEObject
Dim cbNum As Integer
With sht
For Each cb In sht.OLEObjects
If cb.progID Like "Forms.CheckBox*" Then
cbNum = Replace(cb.Name, "Check Box ", vbNullString)
cb.LinkedCell = lnkdCell & cbNum
End If
Next
End With
End Sub
Update
Try something like this, which assumes CheckBoxes are named sequentially according to their index, and that there are no missing indices.
UpdateChkBoxes Sheet4, "ChkBoxOutput!AA"
UpdateChkBoxes Sheet21, "ChkBoxOutput!AB"
UpdateChkBoxes Sheet22, "ChkBoxOutput!AC"
'## Replaced the following error-prone code:
'For i = 1 To .CheckBoxes.Count
' wkbk.Sheet4.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AA" & i
' wkbk.Sheet21.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AB" & i
' wkbk.Sheet22.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AC" & i
' If Err.Number <> 0 Then
'
' End If
'Next i
Then, include this subroutine:
Sub UpdateChkBoxes(sht as Worksheet, lnkdCell as String)
With sht
For i = 1 to .CheckBoxes.Count
.CheckBoxes("Check Box " & i).LinkedCell = lnkdCell & i
Next
End With
End Sub
Original Response
OK, I think the problem is that nothing in your code is actually iterating over the files within a folder. You will need to use a FileSystemObject to do this. You can enable reference to the Microsoft Scripting Runtime dictionary, or, simply declare these variables as generic Object instead of Scripting....
Create an FSO, then assign a folder, and loop over the File objects within this folder. Open the file, and then pass it to a subroutine to perform your checkbox operations.
Something like this:
Option Explicit
Sub LoopFiles()
'## Requires reference to Microsoft Scripting Runtime Library
Dim path As String
Dim fso As New Scripting.FileSystemObject
Dim folder As Scripting.folder
Dim file As Scripting.file
Dim wkbk As Workbook
path = ThisWorkbook.path
Set folder = fso.GetFolder(path)
For Each file In folder.Files
Select Case UCase(Right(file.Name, 4)) '## Make sure you're only working on XLS file types
Case "XLSX", "XLSM", ".XLS" 'etc.
'
Set wkbk = Workbooks.Open(file.Name)
'Now, send this WOrkbook Object to a subroutine
CheckBoxesControl wkbk
wkbk.Save
wkbk.Close
Case Else
'Do nothing
End Select
Next
Set folder = Nothing
Set fso = Nothing
End Sub
Sub CheckBoxesControl(wkbk As Workbook)
Dim i As Long
On Error Resume Next
With wkbk
For i = 1 To 400
.Sheet4.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AA" & i
.Sheet21.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AB" & i
.Sheet22.CheckBoxes("Check Box " & i).LinkedCell = "ChkBoxOutput!AC" & i
Next i
End With
On Error GoTo 0
End Sub