VBA conflicting sheet name checking via an IF function - excel

I have two subroutines that seem to conflict one with each other. Each one of them is for creating and formatting a new worksheet if it doesn't already exist and I ran into a problem where I can only create one of them and not the other (whichever one is the first one I call to create). I found taht it seems to skip the end sub function in an if function:
For Each Worksheet In Application.ActiveWorkbook.Worksheets
Application.DisplayAlerts = False
If Worksheet.Name = "Machine Layout" Then
Exit Sub
Else:
Sheets.Add.Name = "Machine Layout"
End If
Next
And gives an error that sheet with such name already exists.
Both subroutines use this same code except the worksheet name changes.
I'm wondering what's teh problem with that? Could someone, please, help?

Logic is wrong.
set wb = ActiveWorkbook
For Each ws In wb.Worksheets
If ws.Name = "Machine Layout" Then Exit Sub
next ws
Sheets.Add.Name = "Machine Layout"
I did not test my answer...

Related

How to run add-in's (xlam) at opening or close of any workbook that has them installed?

I need to create two "passive" macros that only will work when a certain conditions in a workbook is met (a value in an specific cell).
One macro should run at the openning of the workbook, the other at close.
So, I've created a xlam that does formatting (Create a Report) in which a very hidden tab inputs the value that should trigger the "passive" macros.
I've tried to embed the code inside the Create Report xlam, in the "ThisWorkbook" object. But it's doing nothing since it's trying to run over the xlam. I've already test the codes separately on a test workbook, and since all the codes are embedded in this "test" file, all run correctly.
The reason that I need this to be an xlam or anything similar, is that this will be used by other users and for hundreds of different files.
The code I used for both is something like this:
Disclaimer: the codes were trimmed to show only the main idea of them, so disregard if them had any mistake.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim ws As Worksheet
If DoesSheetExists("SettingsTab") Then
If Worksheets("SettingsTab").Range("G2").Value = "x" Then
'Prevent Computer Screen from running
Application.ScreenUpdating = False
'On closing report we hide all tabs except the welcome screen.
'Access tab is hidden normally and while all others are set to very hidden mode.
For Each ws In Worksheets
If ws.Name = "Welcome" Then
'Skip
Else
If ws.Name = "Access" Then
Application.Worksheets(ws.Name).Visible = xlHidden
Else
Application.Worksheets(ws.Name).Visible = xlVeryHidden
End If
End If
Next ws
Application.Worksheets("Welcome").Select
'Allow Computer Screen to refresh
Application.ScreenUpdating = True
End If
End If
End Sub
And the One for opening looks something like this:
Private Sub Workbook_Open()
If DoesSheetExists("SettingsTab") Then
If Worksheets("SettingsTab").Range("G2").Value = "x" Then
'Prevent Computer Screen from running
Application.ScreenUpdating = False
OpenSheets:
For Each ws In Worksheets
If ws.Name = "Welcome" Then
'Skip
Elseif ws.Name = "Access" Then
'Skip
Elseif ws.Name = "SettingsTab" Then
'Skip
Else
Application.Worksheets(ws.Name).Visible = True
End If
Worksheets(1).Select
Next ws
End If
End If
End Sub
So my question is, What is the right way to implement my two "passive macros"?

Why wont VBA Next go to next Worksheet

So I've been looking on here for a while and I'm trying to make code that goes through every sheet in the workbook unless the sheets are named Summary, Archive, or Template. When running the code it seems to skip the code when I start on a sheet with any of these three names but never goes to the next sheet. (It may be important to mention that my code involves going to another workbook to gather data).
Here's what I have:
For Each rs In ActiveWorkbook.Worksheets
If rs.Name <> "Summary" And rs.Name <> "Archive" And rs.Name <> "Template" Then
'my Code to do
End If
Next rs
Any help would be greatly appreciated.
Thanks
You are using Activesheet or no parent worksheet at all in the actual processing code that you omitted from your question. Use rs instead.
For Each rs In ActiveWorkbook.Worksheets
WITH RS '<~~ use rs
select case lcase(.Name)
case "summary", "archive", "template"
'do nothing
case else
rs.Select '<~~ not considered 'best practice'
'my Code to do with stuff .Range("A1") or .Cells(1, "A") on rs
end select
END WITH
Next rs
Notice that when in a With ... End With you precede all the worksheet members with a . that pushes the parent worksheet to the one described in the With ... End With.
When you meet something unexpected like what you describe, I find it is best to get a visual check of what's going on. Try this:-
Sub Ryan_S()
Dim Ws As Worksheet
For Each Ws In ActiveWorkbook.Worksheets
Debug.Print Ws.Name
If Ws.Name <> "Summary" And Ws.Name <> "Archive" And Ws.Name <> "Template" Then
Debug.Print , "Executing my code"
'my Code to do
End If
Next Ws
End Sub
Except for my little revolt against calling a Worksheet "rs" (which would be a row in my book :-)) the code is exactly like yours. However, I added two lines of code which print something to the Immediate window (Ctl+G if it isn't visible by default). The first line just prints the name of each sheet being called, the second prints only on one of the selected sheets.
This test will guide you toward the error. You might find that you are looping through the wrong workbook (as someone did suggest above), or that your code actually runs but doesn't do what you expect. A common error for beginners is to have On Error Resume Next in it which prevents the code from crashing but doesn't force it to do as instructed.

Avoiding .Activate with ActiveX Form Control

I'm updating a macro that's used in lots of spreadsheets, and it's rather slow. While looking to speed it up, I noticed that at one point it goes through this loop:
For each wsLoop in ThisWorkbook.Worksheets
wsLoop.Activate
With ActiveSheet.Status_Text
If bStatus = True Then
.ForeColor = &HC000&
.Caption = "ONLINE"
Else
.ForeColor = &HFF&
.Caption = "OFFLINE"
End If
End With
Next wsLoop
Where wsLoop is a worksheet, bStatus is a boolean and Status_Text is the name of an ActiveX label form control on each worksheet.
I know using .Activate is bad practice and can slow things down, so I dropped the wsLoop.Activate and changed the next line to With wsLoop.Status_Text, but now I get a "Method or data member not found" error message.
What's the proper way to do this?
Interesting question which seems to touch on some poorly-documented features of Excel VBA. It seems that maybe the expression ActiveSheet.Status_Text is operating as the name of the control, with the dot acting as a namespace qualifier, but that in wsLoop.Status_Text VBA is interpreting the dot as the method/property access operator and is correctly giving the error message that no such method or property exists. To reproduce the problem, I created a label named Status_Text on each sheet and then ran
Sub test1()
Dim ws As Worksheet
For Each ws In Worksheets
Debug.Print ws.Status_Text.Caption 'fails
Next ws
End Sub
It crashes with the error that you show. One workaround (although just why it works is mysterious) is to change the loop index from being a Worksheet to a Variant:
Sub test2()
Dim ws As Variant
For Each ws In Worksheets
Debug.Print ws.Status_Text.Caption 'succeeds
Next ws
End Sub
The odd thing about this last example is if you add the line Debug.Print TypeName(ws) in the for-each loop it prints Worksheet so ws.Status_Text works if ws is a variant which holds a worksheet but not if ws is actually a worksheet. The mystery is in some sense deepened but in another sense lessened when you step through this sub in the debugger, looking at the locals window. The type of ws in the loop is described as Variant/Object/Sheet1 (in the first pass through the loop). The specific sheet seems to be part of the current subtype of the variable.
Another workaround is to use a For-Next loop rather than a For-Each:
Sub test3()
Dim i As Long
For i = 1 To Worksheets.Count
Debug.Print Worksheets(i).Status_Text.Caption 'succeeds
Next i
End Sub
You could use either of these approaches to get a reference to the label without having to activate the sheet.

PivotTable Show Details Destination

I know this may be solved with a more complicated script, but I simply want to have the .ShowDetails action for any PivotTable in my workbook (I have 15+) to send the associated data for a particular Pivot Item to a designated worksheet every time. I have this script, but I believe I have coded something incorrectly (I am receiving a procedure declaration compiling error when I attempt to execute it).
Sub Workbook_SheetBeforeDoubleClick()
Dim WS As Worksheet
If Application.Range(ActiveCell.Address).PivotCell.PivotCellType = xlPivotCellValue Then
For Each WS In ThisWorkbook.Worksheets
If WS.Name = "PivotTable Details" Then
WS.Delete
End If
Next WS
Selection.ShowDetails
ActiveSheet.Name = "PivotTable Details"
End If
End Sub
Couple things...
I believe it should be ShowDetail, not ShowDetails.
ShowDetail
is a property that needs to be set to True if you want to display
the data for the selected cell.
The following line should work.
Selection.ShowDetail = True

How do I make a constant global (work across all my functions) in VBA?

I have gone over most of stack's questions on the matter but can't seem to get it running with my code.
Goal: I have 3 functions: main, fun1, and clear. All three require the use of the datalink as a worksheet type. They are all found in the same module in the same workbook. I set up Workbook_Open in "ThisWorkbook"
Problem: I get the run-time error '424': object required at the "datalink.Cells(1, 10).Value = 0" line
Code:
'this is how I am setting up my global variable
Public datalink As Worksheet
Sub Workbook_Open()
Set datalink = ActiveWorkbook.Worksheets("Sheet1")
End Sub
'deletes all sheets except Sheet1
Sub Clear()
For Each Sheet In Sheets
Application.DisplayAlerts = False
If Sheet.Name <> "Sheet1" Then Sheet.Delete
Next Sheet
datalink.Cells(1, 10).Value = 0
Application.DisplayAlerts = True
'Sheet1.Columns(10).ClearContents
End Sub
EDIT:
I have edited my code above to what I am currently using. I had pasted dated code. My apologies, I should've been clear on that I am trying to prevent from changing all the "Sheet1"s in my modules so I decided to call them datalink, and that way I can just change what "datalink" will be equal to to affect all the module's values for that. Basically, I am trying to make it so I don't have to copy paste the following code in every single document when I change "Sheet1" into some other name
Dim datalink As Worksheet
Set datalink = Sheets("Sheet1") 'master sheet that contains PI data from datalink
First place the line:
Public datalink As Worksheet
as the first line in a standard module
Then:
Sub Clear()
For Each Sheet In Sheets
Application.DisplayAlerts = False
If Sheet.Name <> "Sheet1" Then Sheet.Delete
Next Sheet
datalink.Cells(1, 10).Value = 0
Application.DisplayAlerts = True
datalink.Columns(10).ClearContents
End Sub
Not sure if this is the exact issue but your 'Clear' function is assuming that ActiveSheet is Sheet1. If the ActiveSheet isn't Sheet1 and you try to 'Clear', Sheet1 will be deleted and therefore datalink will be pointing to a sheet that doesn't in fact exist.
Try changing your code to:
If Sheet.Name <> datalink.Name Then Sheet.Delete
Or even better, don't use the Workbook_Open function. It isn't necessary in this instance. Use the object Sheet1 (like you have in your commented out code):
If Sheet.Name <> Sheet1.Name Then Sheet.Delete
Sheet1 can be accessed everywhere as well.

Resources