I have a workbook with many sheets. I am webscraping with numbers, and then making each sheet have the number as the name. I want to display an error if the number has already been given to a sheet. I also want the user to be able to enter a new sheetname if so, but the program keeps popping up its own error message before I can do this.
The number is in cell D10 in the worksheet.
For Each Sheet In ThisWorkbook.Sheets
If Sheet.Name = Range("D10") Then
MsgBox ("ERROR: This Acct No has already been formulated")
NewName = InputBox("Please Rename:")
ActiveSheet.Name = NewName
ElseIf Sheet.Name <> Range("D10") Then
ActiveSheet.Name = Range("D10")
End If
Next Sheet
I expect my own message to pop up, but Excel just pops its own error message.
try this:
Dim MyDuplicate as boolean
MyDuplicate = False
For Each Sheet In ThisWorkbook.Sheets
If Sheet.Name = Range("D10") Then
MsgBox ("ERROR: This Acct No has already been formulated")
NewName = InputBox("Please Rename:")
ActiveSheet.Name = NewName
MyDuplicate = True
Exit for
End If
Next Sheet
If MyDuplicate = False then ActiveSheet.Name = Range("D10")
BTW, I do recommend you avoid using ActiveSheet and assign the sheet to a variable instead.
Option Explicit
Sub TestMe()
Dim wks As Worksheet
Worksheets.Add After:=ActiveSheet
For Each wks In ThisWorkbook.Worksheets
With wks
If .Name = .Range("D10") Then
MsgBox ("ERROR: This Acct No has already been formulated")
.Name = InputBox("Please Rename:")
ElseIf .Name <> .Range("D10") Then
If Trim(.Range("D10")) = "" Then
.Range("D10") = Replace(Replace(Now, ":", "_"), "/", "_")
Application.Wait Now + #12:00:02 AM#
End If
.Name = .Range("D10").Value
End If
End With
Next wks
End Sub
This is some idea how to do it, avoiding the Activate and Select, as per the How to avoid using Select in Excel VBA
(Ironically, I have left Worksheets.Add After:=ActiveSheet)
The part .Range("D10") = Replace(Replace(Now, ":", "_"), "/", "_") writes the current date and time, making sure it is always a unique one, by waiting 2 seconds on the next line - Application.Wait Now + #12:00:02 AM#
Rather than looping every sheet to check for duplicates, create a function that returns a boolean. This function will have an error if the sheet doesn't exist, and no error if the sheet does exist. We check for that error, and return True if sheet exists, False otherwise.
Option Explicit
Private Function SheetExists(wsName As String, Optional wb As Workbook = Nothing) As Boolean
Dim ws As Worksheet
On Error Resume Next
If wb Is Nothing Then
Set ws = Worksheets(wsName)
Else
Set ws = wb.Worksheets(wsName)
End If
SheetExists = (Err.Number = 0)
End Function
And then your code could be replaced with the following, which will keep calling on the InputBox as many times as necessary in order to prevent the user from inputting another invalid/duplicate entry. For this, I've combined the MsgBox and InputBox text, seems unnecessary to throw two prompts at the user when we can use the InputBox to both inform and ask for new input.
Dim ws as Worksheet
Dim newName as String
Set ws = ActiveSheet ' would be better to avoid this, but OK.
newName = Range("D10").Value
While SheetExists(newName, ws.Parent)
newName = InputBox("ERROR: This Acct No has already been formulated!" & vbCrLf & vbCrLf & _
newName & " already exists! Enter new name:")
Wend
ws.Name = newName
Related
Template sheet labeled = "COQ 001"
Log labeled = "Change Order Log"
I have gotten this far: Code below creates a new sheet based off of the template labeled "COQ 001", renames the new worksheet based on user input, goes back to the change order log and sets it to active, inserts a row after the last entry. I think I got that right.
Sub CreateSheet()
Dim xName As String
Dim xSht As Object
On Error Resume Next
xName = InputBox("Please enter new COQ #. For Example: COQ 001", "NEW CHANGE ORDER QUOTE")
If xName = "" Then Exit Sub
Set xSht = Sheets(xName)
If Not xSht Is Nothing Then
MsgBox "Sheet cannot be created as there is already a worksheet with the same name in this
workbook"
Exit Sub
End If
Sheets("COQ 001").Copy after:=Sheets(Sheets.Count)
Set xNWS = Sheets(Sheets.Count)
xNWS.Name = xName
'Link2Log Macro
Worksheets("Change Order Log").Activate
'Locates Last Cell
Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Select
'Inserts Row Below
Range("10:10").EntireRow.Insert
End Sub
To keep going I need it to link specific cells of each newly created worksheet to the change order log sheet since that is my master log. Instead of COQ 001 which shows up on the formula, I would like it to be each newly created sheet.
If anyone can help finish the thought that would be great!!Thanks,
New Formula:
Thanks Nick, this is what the formula looks like:
Sub CreateSheet()
Dim xName As String
Dim xSht As Object
On Error Resume Next
xName = InputBox("Please enter new COQ #. For Example: COQ 001", "NEW
CHANGE ORDER QUOTE")
If xName = "" Then Exit Sub
Set xSht = Sheets(xName)
If Not xSht Is Nothing Then
MsgBox "Sheet cannot be created as there is already a worksheet with
the same name in this workbook"
Exit Sub
End If
Sheets("COQ 001").Copy after:=Sheets(Sheets.Count)
Set xNWS = Sheets(Sheets.Count)
xNWS.Name = xName
'Link2Log Macro
Worksheets("Change Order Log").Activate
Range("10:10").EntireRow.Insert
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
ActiveCell.FormulaR1C1 = "='xName'!R[-3]C[5]"
Range("C10").Select
ActiveCell.FormulaR1C1 = "='xName'!R[2]C:R[2]C[5]"
Range("D10").Select
ActiveCell.FormulaR1C1 = "='xName'!R[-2]C[3]"
Range("E10").Select
ActiveCell.FormulaR1C1 = "='xName'!R[-1]C[2]"
Range("G10").Select
ActiveCell.FormulaR1C1 = "='xName'!R[40]C"
Range("G11").Select
End Sub
But I get an error on the log:
LOG
You can try this:
Sub CreateSheet()
Dim xName As String, wb As Workbook
Dim ws As Worksheet
xName = InputBox("Please enter new COQ #. For Example: COQ 001", _
"NEWCHANGE ORDER QUOTE")
If xName = "" Then Exit Sub
Set wb = ThisWorkbook
If SheetExists(xName, wb) Then
MsgBox "Sheet '" & xName & "' cannot be created as there is already " & _
"a worksheet with the same name in this workbook"
Exit Sub
End If
wb.Worksheets("COQ 001").Copy after:=Sheets(Sheets.Count)
Set ws = wb.Sheets(Sheets.Count)
ws.Name = xName
With wb.Worksheets("Change Order Log")
.Activate
.Cells(Rows.Count, "B").End(xlUp).Offset(1).EntireRow.Insert 'add a row to the table
With .Cells(Rows.Count, "B").End(xlUp).Offset(1).EntireRow
.Columns("B").Formula = "='" & xName & "'!G7"
.Columns("C").Formula = "='" & xName & "'!C12" 'only need first cell of merged area
.Columns("D").Formula = "='" & xName & "'!G8"
.Columns("E").Formula = "='" & xName & "'!G9"
.Columns("G").Formula = "='" & xName & "'!G50" 'likewise
End With
End With
End Sub
Function SheetExists(SheetName As String, Optional wb As Excel.Workbook)
Dim s As Excel.Worksheet
If wb Is Nothing Then Set wb = ThisWorkbook
On Error Resume Next
Set s = wb.Sheets(SheetName)
On Error GoTo 0
SheetExists = Not s Is Nothing
End Function
Note you don't need to select/activate ranges to work with them - just reference them directly.
I'm adding a new worksheet to my workbook with
Application.ScreenUpdating = False
SheetExists = False
For Each WS In Worksheets
If WS.Name = "BLANK" Then
SheetExists = True
End If
Next WS
If Not SheetExists Then
Sheets.Add
ActiveSheet.Name = "BLANK"
End If
Is there any way to sheets.add silently without bringing focus to or activating the new added sheet? I just want to stay on the sheet (ie. Sheet1) that is currently active and add the new sheet in the background.
Thanks
At first, things look simple but there are a few things to consider:
There could be more sheets selected before running the code
The selected sheet(s) could be Chart sheet(s)
The Workbook can be protected
You might not want to set Application.ScreenUpdating = True at the end of the method because you might be running this from within another method that still needs it off
Restoring selection can only happen if the proper window is activated
You could use this method:
Sub AddWorksheet(ByVal targetBook As Workbook, ByVal sheetname As String)
Const methodName As String = "AddWorksheet"
'Do input checks
If targetBook Is Nothing Then
Err.Raise 91, methodName, "Target Book not set"
ElseIf sheetname = vbNullString Then
Err.Raise 5, methodName, "Sheet name cannot be blank"
ElseIf Len(sheetname) > 31 Then
Err.Raise 5, methodName, "Sheet name cannot exceed 31 characters"
Else
Dim arrForbiddenChars() As Variant
Dim forbiddenChar As Variant
arrForbiddenChars = Array(":", "\", "/", "?", "*", "[", "]")
For Each forbiddenChar In arrForbiddenChars
If InStr(1, sheetname, forbiddenChar) > 0 Then
Err.Raise 5, methodName, "Sheet name cannot contain characters: : \ / ? * [ or ]"
End If
Next forbiddenChar
End If
Dim alreadyExists As Boolean
'Check if a sheet already exists with the desired name
On Error Resume Next
alreadyExists = Not (targetBook.Sheets(sheetname) Is Nothing)
On Error GoTo 0
If alreadyExists Then
MsgBox "A sheet named <" & sheetname & "> already exists!", vbInformation, "Cancelled" 'Can remove
Exit Sub
End If
'Check if Workbook is protected
If targetBook.ProtectStructure Then
'Maybe write code to ask for password and then unprotect
'
'
'Or simply exit
MsgBox "Workbook is protected. Cannot add sheet", vbInformation, "Cancelled"
Exit Sub
End If
Dim bookActiveWindow As Window
Dim appActiveWindow As Window
Dim selectedSheets As Sheets
Dim screenUpdate As Boolean
Dim newWSheet As Worksheet
'Store state
Set bookActiveWindow = targetBook.Windows(1)
Set appActiveWindow = Application.ActiveWindow 'Can be different from the target book window
Set selectedSheets = bookActiveWindow.selectedSheets
screenUpdate = Application.ScreenUpdating
'Do main logic
screenUpdate = False
If bookActiveWindow.Hwnd <> Application.ActiveWindow.Hwnd Then
bookActiveWindow.Activate
End If
If selectedSheets.Count > 1 Then selectedSheets(1).Select Replace:=True
Set newWSheet = targetBook.Worksheets.Add
newWSheet.Name = sheetname
'Restore state
selectedSheets.Select Replace:=True
If appActiveWindow.Hwnd <> Application.ActiveWindow.Hwnd Then
appActiveWindow.Activate
End If
Application.ScreenUpdating = screenUpdate
End Sub
If you want the book containing the code then you can call with:
Sub Test()
AddWorksheet ThisWorkbook, "BLANK"
End Sub
or, if you want the currently active book (assuming you are running this from an add-in) then you can call with:
Sub Test()
AddWorksheet ActiveWorkbook, "BLANK"
End Sub
or any other book depending on your needs.
Just remember who was active:
Sub ytrewq()
Dim wsh As Worksheet, SheetsExist As Boolean
Set wsh = ActiveSheet
Application.ScreenUpdating = False
SheetExists = False
For Each ws In Worksheets
If ws.Name = "BLANK" Then
SheetExists = True
End If
Next ws
If Not SheetExists Then
Sheets.Add
ActiveSheet.Name = "BLANK"
End If
wsh.Activate
Application.ScreenUpdating = False
End Sub
I'm having an issue with below. My code is basically a sorting function, where it pulls data from another sheet in order to give the user the correct view, however if the user selects the same product twice, my code produces an error since the information sheet is already in the workbook.
Therefore I simply want it to select the sheet, if the sheet is already in the workbook instead of running the code once more.
My issue is, that sometimes the sheet name will have more than 31 characters, which is why I use the left/len function. The problem is that it doesn't recognize the existing sheet name as
Left(Myvalue & " Case Types", 31)
and therefore just runs the code even though sheet already exist and therefore produces an error. When tabbing through the code with F8 I can see that the name should be same. Any suggestions?
Dim S As Worksheet
Myvalue = activecell.Value
For Each S In ActiveWorkbook.Worksheets
If S.Name = Left(Myvalue & " Case Types", 31) Then GoTo Sheetalreadyexist
Next S
'Create New Sheet
Set ws = Sheets.Add(after:=Sheets(Worksheets.Count))
If Len(Myvalue & " Case Types") > 31 Then
ws.Name = Left(Myvalue & " Case Types", 31)
Else: ws.Name = Myvalue & " Case Types"
End If
Sheetalreadyexist: sheets(Left(Myvalue & " Case Types", 31).select
I would use the following function to check if a sheet exists or not
Public Function sheetExists(SheetName As String, Optional wrkBook As Workbook) As Boolean
If wrkBook Is Nothing Then
Set wrkBook = ActiveWorkbook 'or ThisWorkbook - whichever appropriate
End If
On Error GoTo EH
sheetExists = False ' Not really neccessary as this is the default
Dim sht As Object
For Each sht In wrkBook.Sheets
If UCase(sht.Name) = UCase(SheetName) Then
sheetExists = True
Exit For
End If
Next sht
Exit Function
EH:
sheetExists = False
End Function
Then your code would look like that
Sub OP_Code()
Dim S As Worksheet
Myvalue = ActiveCell.Value
If sheetExists(Left(Myvalue & " Case Types", 31)) Then
Sheets(Left(Myvalue & " Case Types", 31)).Select
Else
'Create New Sheet
Set ws = Sheets.Add(after:=Sheets(Worksheets.Count))
If Len(Myvalue & " Case Types") > 31 Then
ws.Name = Left(Myvalue & " Case Types", 31)
Else: ws.Name = Myvalue & " Case Types"
End If
End If
End Sub
Quicker version
Public Function sheetExists(SheetName As String, Optional wrkBook As Workbook) As Boolean
If wrkBook Is Nothing Then
Set wrkBook = ActiveWorkbook 'or ThisWorkbook - whichever appropriate
End If
On Error GoTo EH
Dim sht As Worksheet
set sht = wrkbook.worksheets(sheetname) 'will cause error if sheetname doesn't exist
Set sht = nothing
SheetExists=true
Exit Function
EH:
sheetExists = False
End Function
I'm setting up a workbook that has two sheets. One sheet is for a data set and the second sheet is for analysis.
The data set sheet will be first (on the left/Sheet1) followed by the analysis sheet second (on the right/Sheet2).
Each sheet Name will have today's date and a title.
I would like to check if both sheets are present for today's date.
If Sheet1 is missing, add on the left.
If Sheet2 is missing, add on the right.
If both are missing, add both.
There should be no other sheets.
I have two modules. One checks for one sheet, and one checks for the other.
Option Explicit
Public szTodayRtsMU As String
Dim szTodayRawData As String
' Add and name a sheet with today's date.
Sub AddRtsMUsSheets_Today()
' Date and title.
szTodayRtsMU = Format(Date, "dd-mm-yyyy") & " " & "Rts & MUs"
On Error GoTo MakeSheet
' Check if sheet already exists, if it does, select activate it.
Sheets(szTodayRtsMU).Activate
' No errors, code is done.
Exit Sub
MakeSheet:
' If the sheet doesn't exist, add it.
ActiveWorkbook.Sheets.Add After:=Worksheets(Worksheets.Count)
' Name it
ActiveSheet.Name = szTodayRtsMU
End Sub
Sub AddRawDataSheets_Today()
' Date and title.
szTodayRawData = Format(Date, "dd-mm-yyyy") & " " & "Raw Data"
On Error GoTo MakeSheet
' Check if sheet already exists, if it does, select activate it.
Sheets(szTodayRawData).Activate
' No errors, code is done.
Exit Sub
MakeSheet:
' If the sheet doesn't exist, add it.
ActiveWorkbook.Sheets.Add Before:=Worksheets(Worksheets.Count)
' Name it
ActiveSheet.Name = szTodayRawData
End Sub
Tested, 100% working:
Option Explicit
Sub CheckForWorksheets()
Dim szTodayRawData As String
Dim szTodayRtsMU As String
Dim ws As Worksheet
Dim countRawData As Byte 'check if exists the RawData sheet
Dim countRTsMU As Byte 'check if exists the RtsMU sheet
'Date and titles
szTodayRawData = Format(Date, "dd-mm-yyyy") & " " & "Raw Data"
szTodayRtsMU = Format(Date, "dd-mm-yyyy") & " " & "Rts & MUs"
'Initialize the counters with 1
countRawData = 1
countRTsMU = 1
'This is a loop on all the worksheets on this workbook
For Each ws In ThisWorkbook.Worksheets
'If the sheets exists then the counter goes to 0
If ws.Name = szTodayRawData Then
countRawData = 0
ElseIf ws.Name = szTodayRtsMU Then
countRTsMU = 0
End If
Next ws
'Add the sheets if needed
With ThisWorkbook
If countRawData = 1 Then
Set ws = .Sheets.Add(before:=.Sheets(.Sheets.Count))
ws.Name = szTodayRawData
End If
If countRTsMU = 1 Then
Set ws = .Sheets.Add(After:=.Sheets(.Sheets.Count))
ws.Name = szTodayRtsMU
End If
End With
'Delete any other sheet
For Each ws In ThisWorkbook.Sheets
If Not ws.Name = szTodayRawData And Not ws.Name = szTodayRtsMU Then
Application.DisplayAlerts = False
ws.Delete
Application.DisplayAlerts = True
End If
Next ws
End Sub
If you need help understanding the code ask me anything.
How can i rename a sheet and add a number to the end of the name if the name already exists.
I'm using this code but need to add a number to the end of sheet name if name already exists.
VBA_BlankBidSheet.Copy After:=ActiveSheet
ActiveSheet.Name = "New Name"
The code below loops through all worksheets in ThisWorkbook and checks if there is already a sheet with a name of "New Name", if it does it adds a number at the end.
Sub RenameSheet()
Dim Sht As Worksheet
Dim NewSht As Worksheet
Dim VBA_BlankBidSheet As Worksheet
Dim newShtName As String
' modify to your sheet's name
Set VBA_BlankBidSheet = Sheets("Sheet1")
VBA_BlankBidSheet.Copy After:=ActiveSheet
Set NewSht = ActiveSheet
' you can change it to your needs, or add an InputBox to select the Sheet's name
newShtName = "New Name"
For Each Sht In ThisWorkbook.Sheets
If Sht.Name = "New Name" Then
newShtName = "New Name" & "_" & ThisWorkbook.Sheets.Count
End If
Next Sht
NewSht.Name = newShtName
End Sub
The test procedure on a new workbook will generate these sheet names:
Sheet1_1, Sheet2_1 and ABC.
If Sheet1_1 exists and we ask for a new Sheet1 it will return Sheet1_2, as ABC doesn't exist in a new workbook it will return ABC.
The Test code adds a new sheet called 'DEF'. If you run it a second time it will create 'DEF_1'.
Sub Test()
Debug.Print RenameSheet("Sheet1")
Debug.Print RenameSheet("Sheet2")
Debug.Print RenameSheet("ABC")
Dim wrkSht As Worksheet
Set wrkSht = Worksheets.Add
wrkSht.Name = RenameSheet("DEF")
End Sub
Public Function RenameSheet(SheetName As String, Optional Book As Workbook) As String
Dim lCounter As Long
Dim wrkSht As Worksheet
If Book Is Nothing Then
Set Book = ThisWorkbook
End If
lCounter = 0
On Error Resume Next
Do
'Try and set a reference to the worksheet.
Set wrkSht = Book.Worksheets(SheetName & IIf(lCounter > 0, "_" & lCounter, ""))
If Err.Number <> 0 Then
'If an error occurs then the sheet name doesn't exist and we can use it.
RenameSheet = SheetName & IIf(lCounter > 0, "_" & lCounter, "")
Exit Do
End If
Err.Clear
'If the sheet name does exist increment the counter and try again.
lCounter = lCounter + 1
Loop
On Error GoTo 0
End Function
Edit: Removed the Do While bNotExists as I wasn't checking bNotExists - just using Exit Do instead.
Building on Darren's answer, I thought it might be easier to just rename the sheet right away instead of returning the name that can be used. I also refactored a bit. Here's my take:
Private Sub nameNewSheet(sheetName As String, newSheet As Worksheet)
Dim named As Boolean, counter As Long
On Error Resume Next
'try to name the sheet. If name is already taken, start looping
newSheet.Name = sheetName
If Err Then
If Err.Number = 1004 Then 'name already used
Err.Clear
Else 'unexpected error
GoTo nameNewSheet_Error
End If
Else
Exit Sub
End If
named = False
counter = 1
Do
newSheet.Name = sheetName & counter
If Err Then
If Err.Number = 1004 Then 'name already used
Err.Clear
counter = counter + 1 'increment the number until the sheet can be named
Else 'unexpected error
GoTo nameNewSheet_Error
End If
Else
named = True
End If
Loop While Not named
On Error GoTo 0
Exit Sub
nameNewSheet_Error:
'add errorhandler here
End Sub
The .net version of VB uses the Try ... Catch formulation to catch runtime errors, see {https://msdn.microsoft.com/en-us/library/ms973849.aspx}(https://msdn.microsoft.com/en-us/library/ms973849.aspx) for a comparison with the old "on error" formulation of VB6 and before. It is better suited to do what you want, and will make shorter exception runs imho.
I'm trying to find out what exception is thrown when trying to rename to an existing sheetname, and will edit here to a workable script when i find it.