Reorder hidden tabs in excel - vba

At work, we've developed a tool using Excel and VBA. This tool has hidden sheets that will only be opened once the previous step is complete. One of the issues I'm running into from the previous coder is that the very last step, there is an extra button, let's call it A, that can be clicked. Based on the order of sheets the previous coder created, this sheet was second out of 10, and when A is clicked, its automatically goes to the second position.
Is there any way I can modify it to the the right most tab?
The problem I run into is when I get to the final step, I can manually move the tab to the right hand side, but that is only after I have finished my analysis, and can not go to the beginning, so it does not allow me to save.

This will move your hidden sheet to end of all visible sheets:
Sub test()
With Sheets("Sheet1")
.Visible = True
Sheets("Sheet1").Move After:=Sheets(Sheets.Count)
.Visible = False
End With
End Sub
And this will move your hidden sheet to the end of all hidden and visible sheets:
Sub moveHiddenSheet()
Dim ws, x, lastSheet
x = 0
For ws = Worksheets.Count To 0 Step -1
x = x + 1
If Sheets(Worksheets.Count - x).Visible = False Then
Sheets(ws).Visible = xlSheetVisible
lastSheet = Sheets(ws).Name
Exit For
End If
Next ws
With Sheets("Sheet1")
.Visible = True
Sheets("Sheet1").Move After:=Sheets(Worksheets.Count)
.Visible = False
End With
Sheets(lastSheet).Visible = False
End Sub

Related

VBA and Bloomberg Aplication.Run "RefreshAllWorkbooks"

Please advise on the following:
I have a code that should refresh formulas in my workbook and then copy the results as values to the other sheet. The problem is that the code flies right through everything and copies data even before the refresh is completed (refresh takes approx. 10-15 seconds).
I have used the below (and its varieties) but it didn't do anything to slow it down:
Application.Wait (Now + TimeValue("0:00:15"))
Do I have to do a loop that would check the cell content and execute "copy" part once it identifies the cell is updated?
Sub Excel_VBA_Timer_Event1()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim V As Workbook: Dim x, y, z As Worksheet
Set V = ThisWorkbook: Set x = V.Sheets(1): Set y = V.Sheets(2): Set z = V.Sheets(3)
z.Columns("A:O").EntireColumn.Delete
Aplication.Run "RefreshAllWorkbooks"
'WAIT HERE for approx 15 seonds
'At the moment the code flies through till the end before Bloomberg formulas are refreshed
y.Columns("C:M").Copy
z.Range("A1").PasteSpecial (xlPasteValues): z.Range("A1").PasteSpecial (xlPasteFormats)
z.Activate: z.Cells(1, 1).Select
Application.CutCopyMode = False
MsgBox "Done"
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub

Loop to hide sheets

Currently, I am using this VBA code to hide all sheets in my spreadsheet:
Sub HideSheets()
Sheet1.Visible = False
Sheet2.Visible = False
Sheet3.Visible = False
Sheet4.Visible = True
End Sub
This code runs perfectly.
However, since I have more than just 4 sheets in my original file I would like to have a solution with a loop. Therefore, I tried to go with the following formula:
Sub LoopHideSheets()
Dim b As Worksheet
For Each b In Worksheets
b.Select
ActiveWindow.Visible = False
Next b
End Sub
Once I run this code my Excel file crashes. I guess the reason for this is that at least one file needs to stay visible. Do you know what I have to change my loop code so all sheets getting hidden instead of Sheet4?
Sub LoopHideSheets()
Dim b As Worksheet
For Each b In Worksheets
If b.Name <> "DontHide" Then 'whatever the sheet name is to not hide
b.Visible = False
End If
Next b
End Sub
This will hide every sheet that is not named "Sheet4" - but be careful, you need to ensure Sheet4 exists or you will get an error.
Sub LoopHideSheets()
Dim b As Worksheet
For Each b In Worksheets
If b.Name <> "Sheet4" Then b.Visible = False
Next b
End Sub
You might want to hide all sheets other than the one currently active..?
If b.Name <> ActiveSheet.Name Then b.Visible = False
However, you may need to hide all but 1 (hey, I've no idea why) as per other answers. To do this properly, you need to count visible sheets and only deal with those:
Sub LoopHideSheets()
Dim b As Worksheet, shtcnt As Long
'Count up all visible sheets
For Each b In Worksheets
If b.Visible = True Then shtcnt = shtcnt + 1
Next b
'Hide each visible sheet until only 1 is left
For Each b In Worksheets
If b.Visible = True And shtcnt > 1 Then
shtcnt = shtcnt - 1
b.Visible = False
End If
Next b
End Sub
Alternatively you can catch the error with error handling
Sub HideAllSheets()
Dim b As Worksheet
For Each b In Worksheets
On Error Resume Next 'disable error reporting
b.Visible = False
If Err.Number = 1004 Then
MsgBox "The last sheet must stay visible" 'remove if you don't want a message
Exit Sub
End If
On Error GoTo 0 're-enable error handling. Don't forget this line!
Next b
End Sub
If You always want to have last sheet visible you could use this
Sub HideSheets()
Dim i As Long
With ThisWorkbook
For i = 1 To .Sheets.Count - 1
.Sheets(i).Visible = False
Next i
End With
End Sub

changing sheet with macro causes sheet to freeze

My macro for changing sheets doesn't work anymore after I created a few UserForms. Thought the problem was the UserForms were not unloading properly but that doesn't seem to be the case.
When I click the button (which resides in a custom pop up menu) to go to another sheet it works but the sheet acts frozen, I can't scroll or highlight for example.
But if i change sheets by clicking on the tabs then everything is fine. So it would appear that my code to active another sheet is faulty.
My excel workbook has some UserForms, Worksheet_Change and Worksheet_SelectionChange events if that matters.
Is there a better way of doing this. Something that will obviously work and note freeze my sheet after it is activated?
Sheet goto Code:
Sub goto630()
Application.ScreenUpdating = False
Dim sht2 As Worksheet
Set sht2 = ThisWorkbook.Worksheets(2)
Dim sht3 As Worksheet
Set sht3 = ThisWorkbook.Worksheets(3)
sht3.Activate
sht3.Protect _
DrawingObjects:=False, _
Contents:=True, _
Scenarios:=False, _
UserInterFaceOnly:=True, _
AllowFormattingCells:=True
sht2.Visible = True
sht3.Visible = True
On Error GoTo 0
ActiveWindow.Zoom = 90
ActiveWindow.DisplayHeadings = True
ActiveWindow.DisplayHorizontalScrollBar = True
ActiveWindow.DisplayVerticalScrollBar = True
Application.DisplayFormulaBar = True
sht3.DisplayPageBreaks = False
ThisWorkbook.Worksheets(1).Visible = False
ThisWorkbook.Worksheets(4).Visible = False
ThisWorkbook.Worksheets(5).Visible = False
ThisWorkbook.Worksheets(6).Visible = False
ThisWorkbook.Worksheets(7).Visible = False
ThisWorkbook.Worksheets(8).Visible = False
Set sht2 = Nothing
Set sht3 = Nothing
Application.ScreenUpdating = True
End Sub
UserForm Code:
Private Sub UserForm_Click()
Application.OnTime Now + TimeValue("00:00:01"), "Finish"
Unload Me
Call Module2.ScreenRefresh
Exit Sub
End Sub
ScreenRefresh Module is just Application.ScreenUpdating = True read somewhere about this same problem that if the ScreenUpdating is in another module then when called it will fix the freeze issue.
Finish Module is activating the sheet again after the UserForm has been closed (also said to help the freeze issue).
It seems that Excel is waiting for me to click a cell before allowing me to scroll. How can I get around this?
Thanks for any help :)

Modifying ActiveX controls through custom add-in in Excel

I'm working on moving a worksheet into an add-in so I can make updates to the code without having to give new workbooks to everyone. The process has been fairly straightforward until I got to the area where add-in code needs to modify ActiveX controls present on the sheet.
The previous code I was using to modify these:
If Sheet1.Range(RowHighlightToggle.LinkedCell).Value = True Then
RowHighlightToggle.Caption = "Row Highlight - On"
HighlightStatus = 0
Else
RowHighlightToggle.Caption = "Row Highlight - Off"
HighlightStatus = 1
End If
RowHightlightToggle being the ActiveX control in question. I'm not sure how to refer to this button when coding inside the add-in. I've tried doing Sheet1.RowHighlightToggle.LinkedCell and that is giving me an error as well. I'm not using Sheet1 inside the add-in as I have a function to get codenames from the target workbook so Sheet1 is usually something like AWSheet1 but it is a Worksheet variable so that is not the issue either. I can read the linked cell value quite easy but I have no way of changing the button caption without somehow referring to the button inside the code.
This button will always be present in the workbook that this add-in is being made for, I have additional code to make sure the add-in is only visible in that workbook as well and hides itself for any others.
Is there a way to refer to the button through the add-in or possibly a way to link the caption to a cell so I can change the cell value to update the caption?
After a bit more research I found out I can refer to it by using OLEObjects, working code including the rest of the sub is below.
Sub RowHighlightToggle()
'-----Startup Code--------
With Application
.ScreenUpdating = False
.DisplayStatusBar = False
.DisplayAlerts = False
End With
'------------------------
Dim HighlightStatus As Long, AWSheet1 As Worksheet, ThisButton As Object
If TargetWorkbook Is Nothing Then Set TargetWorkbook = ActiveWorkbook
Set AWSheet1 = GetWsFromCodeName(TargetWorkbook, "Sheet1")
Set ThisButton = AWSheet1.OLEObjects("RowHighlightToggle")
Call Common_Functions.StartUnlock
If AWSheet1.Range(ThisButton.LinkedCell).Value = True Then
ThisButton.Object.Caption = "Row Highlight - On"
HighlightStatus = 0
Else
ThisButton.Object.Caption = "Row Highlight - Off"
HighlightStatus = 1
End If
Call Common_Functions.StartLock
If Worksheets.Count > 6 Then
Call Common_Functions.SheetArrayBuild(TargetWorkbook)
For i = LBound(SheetArray) To UBound(SheetArray)
Sheets(SheetArray(i, 1)).Range("Z1").Value = HighlightStatus
Next i
End If
'-----Finish Code--------
With Application
.ScreenUpdating = True
.DisplayStatusBar = True
.DisplayAlerts = True
.EnableEvents = True
End With
'------------------------
End Sub
And the function to get the worksheet from the workbook
Function GetWsFromCodeName(wb As Workbook, CodeName As String) As Excel.Worksheet
Dim ws As Excel.Worksheet
For Each ws In wb.Worksheets
If ws.CodeName = CodeName Then
Set GetWsFromCodeName = ws
Exit For
End If
Next ws
End Function
Assuming the control is on Sheet1, you should be able to use:
Sheet1.RowHightlightToggle.Caption = "Row Highlight - On"
But you can also get at the control using the shapes collection:
Sheet1.Shapes("RowHightlightToggle").DrawingObject.Object.Caption = "Row Highlight - On"
Or, with a more generic workbook variable:
Dim userWorkbook as Workbook
Set userWorkbook = Workbooks("UserData.xlsm")
userWorkbook.Worksheets("Foo").Shapes("RowHightlightToggle").DrawingObject.Object.Caption = "Row Highlight - On"

Excel VBA worksheet event to toggle between hide / unhide

I am working on a project to minimise the number of sheets that are visible in a an excel workbook at any one given time. I am trying to create a parent sheet (which acts as a toggle button) to show/hide child sheets. For example I have a 6 sheets in a workbook: Inputs, Input 1 and Input 2, Outputs, Output 1 & Output 2. Inputs and Outputs will toggle hide and unhide the others worksheets when activated. I have created 2 worksheet level Sub Functions to try and do this. The first one works great, but the other one only works if the first one has been activated and hidden again. Any advice on a better way to do this would be great. I am not sure why excel doesn't have this function already. I tried with arrays, but that doesn't seem to work. I think you need to unhide each tab individually.
'1. Inputs:
Private Sub Worksheet_Activate()
On Error Resume Next
Sheets("Input 1").Visible = True = Not Sheets("Input 1").Visible = True
Sheets("Input 2").Visible = True = Not Sheets("Input 2").Visible = True
Sheets("Input 1").Activate 'needed to deactivate inputs sheet
End Sub
'2. Outputs
Private Sub Worksheet_Activate()
On Error Resume Next
Sheets("Output 1").Visible = True = Not Sheets("Output 1").Visible = True
Sheets("Output 2").Visible = True = Not Sheets("Output 2").Visible = True
Sheets("Output 1").Activate 'needed to deactivate Outputs sheet
End Sub
Here's a link to a file you can download from onedrive:
https://1drv.ms/x/s!Ah_zTnaUo4DzjhWzQ3OTq9tq1APC
Rather than hard-code what should happen when each sheet is selected, I've used a ListObject (i.e. an Excel Table) on a 'Controls' sheet to store the relationships between 'parent' sheets and their various 'children'. The code simply checks this ListObject to see which children belong to which parent, and then takes action accordingly. This has the added bonus of making it very easy for someone who doesn't understand VBA in the slightest to add or amend those Parent/Child sheet relationships in need.
I've also implemented a 'Developer' mode in which the sheet hiding does NOT happen. Nothing more frustrating than trying to do development on an application that treats you like a mere 'user' :-) You can toggle it between 'User' and 'Developer' mode by using the keyboard shortcut Ctrl + Shift + D (D for Developer).
Here's how that looks in the sample file I just put together. I've added the ListObject shown below into a new sheet called 'Controls', and given the ListObject the name of 'VisibleSheets':
I've also added a named range called DeveloperMode, with a value of TRUE:
Here's the code that toggles the application between 'User' mode and 'Developer' mode, that goes in a standard code module:
Public Sub ToggleDeveloperMode()
Dim ws As Worksheet
If ActiveWorkbook.Names("DeveloperMode").Value = "=TRUE" Then
ActiveWorkbook.Names("DeveloperMode").Value = "=FALSE"
Else
ActiveWorkbook.Names("DeveloperMode").Value = "=TRUE"
For Each ws In ActiveWorkbook.Worksheets
ws.Visible = xlSheetVisible
Next ws
End If
End Sub
Here's the code that actually does all the hiding and unhiding, that also goes in a standard code module:
Sub DisplaySheets()
Dim ws As Worksheet
Dim lo As ListObject
Dim lc As ListColumn
Dim vMatch As Variant
Set lo = Range("VisibleSheets").ListObject
If Not [DeveloperMode] Then
For Each lc In lo.ListColumns
If lc.Name = ActiveSheet.Name Then
For Each ws In ActiveWorkbook.Worksheets
Set vMatch = Nothing 'Reset from last pass
vMatch = Application.Match(ws.Name, lo.HeaderRowRange, 0)
If IsError(vMatch) Then 'It's not one of our main sheets
Set vMatch = Nothing 'Reset from last pass
vMatch = Application.Match(ws.Name, lc.Range, 0)
If IsError(vMatch) Then
ws.Visible = xlSheetVeryHidden
Else
ws.Visible = xlSheetVisible
End If
End If
Next ws
End If
Next lc
End If
End Sub
Here's a snippet of code that goes in the ThisWorkbook module that assigns the Keyboard Shortcut of Ctrl + Shift + D to the ToggleDeveloperMode routine so that you can easily toggle between modes. (Don't tell users what this keyboard shortcut is):
Private Sub Workbook_Open()
Application.OnKey "^+D", "ToggleDeveloperMode"
End Sub
And lastly, here's the code triggers the DisplaySheets routine, that also goes in the ThisWorkbook module:
Private Sub Workbook_SheetActivate(ByVal Sh As Object)
DisplaySheets
End Sub
It works a treat. Here's what I see when I select each of the 3 Parent sheets in turn:
...and here's what happens when I use the Ctrl + Shift + D shortcut to put the app into 'Developer' mode, ALL sheets are unhidden, including the one with the controls on it.
I'd suggest giving the parent tabs the same color as I have here, so that it's easier for users to understand that they don't change regardless of the other tabs that selectively appear/disappear.
If there's any chance that users (or you) might want to rename the sheets, use codenames instead of sheetnames. Let me know if you're not sure what I'm talking about.
As per user3598756, this question may need some clarification, however it sounds like you trying to emulate behaviour similar to this:
Action Visible Worksheet
------ -----------------
Open Workbook [Input], [Output]
Activate [Input] [Input], [Output], [Input1], [Input2] ' (shows InputX)
Activate [Input1] [Input], [Output], [Input1], [Input2] ' (no change)
Activate [Output] [Input], [Output], [Output1], [Output2] ' (hides InputX, shows OutputX)
This makes [Input] and [Output] your only gateway worksheets, so the following on [Input] (and the converse for [Output]) would achieve this.
Private Sub Worksheet_Activate()
Sheets("Input 1").Visible = True
Sheets("Input 2").Visible = True
Sheets("Output 1").Visible = False
Sheets("Output 2").Visible = False
End Sub
Notes
Avoid using On Error Resume Next unless you have a specific reason to. It's usually a good thing that your code grinds to a halt if there is something wrong, as opposed to keeping its little secrets to itself and leaving you none the wiser.
The .Visible property is itself a Boolean, so the conditional .Visible = True is equivalent to using just .Visible by itself.
Your .Visible statements are probably not resolving how you intended them to. Only one of your = on each line will be an assignment operator, the others will be equality checks. In the absence of brackets, it will be your first =, and the other = will be step-by-step equality checks working from the right to the left. This is operator precedence at work.
Regarding the last point, say sheet Input 1 is visible, your first line will resolve as:
Sheets("Input 1").Visible = True = Not Sheets("Input 1").Visible = True
Sheets("Input 1").Visible = True = Not <True> = True
Sheets("Input 1").Visible = True = Not <True>
Sheets("Input 1").Visible = True = <False>
Sheets("Input 1").Visible = <False>
Where what I believe was intended is:
Sheets("Input 1").Visible = Not Sheets("Input 1").Visible ' i.e. toggle my visibility
These things can be hard to pick up with booleans, because even if your logic is wrong, the result can be right 'half' of the time.
I have managed to make this work. The issue was had something to do with trying to use the "Outputs" Private Sub Worksheet_Activate() function when the "Input 1" sheet was activated and hidden. I have added another tab called "Main" to replace this, so "Main" will always be the active tab after the function has been activated. This resolves the issue although it would be better if the focus didn't jump around the workbook while navigating what in each "folder". Here is the updated code...
'Inputs "Parent folder" sheet
Private Sub Worksheet_Activate()
On Error Resume Next
Sheets("Input 1").Visible = True = Not Sheets("Input 1").Visible = True
Sheets("Input 2").Visible = True = Not Sheets("Input 2").Visible = True
**Sheets("Main").Activate** 'needed to deactivate Inputs sheet
End Sub
'Outputs "Parent folder" sheet
Private Sub Worksheet_Activate()
On Error Resume Next
Sheets("Output 1").Visible = True = Not Sheets("Output 1").Visible = True
Sheets("Output 2").Visible = True = Not Sheets("Output 2").Visible = True
**Sheets("Main").Activate** 'needed to deactivate Outputs sheet
End Sub
I think this is a great way to simplify workbooks with may tabs. It could defiantly be improved so look forward to hearing any suggestions.
Here is a link to my working file
https://1drv.ms/x/s!AvtNNMCst1bIgxjCBCemZlCerHMo