How to apply workbook_open to multiple sheets - vba

I have a excel workbook that a number of users interact with daily and on multiple montiors with different resolutions, screen zooms etc.. I need all worksheets to adjust to the ranges on each sheet I want the user to see each time.
Below works for 1 worksheet, but how would I get it to apply to all worksheets (Sheet1,Sheet2,etc.)
Private Sub Workbook_Open()
With Sheets("Sheet1")
Columns("A:P").Select
ActiveWindow.Zoom = True
Range("A1").Select
End With
End Sub

You can use the Worksheet_Activate event, and place code such as
Private Sub Worksheet_Activate()
Columns("A:P").Select
ActiveWindow.Zoom = True
Range("A1").Select
End Sub
on each sheet, editing the range as required.
That code will execute every time the sheet is activated, which may or may not be what you would like, so you may need to use something a bit more complicated and use:
Private AlreadyRun As Boolean
Private Sub Worksheet_Activate()
If Not AlreadyRun Then
Columns("A:P").Select
ActiveWindow.Zoom = True
Range("A1").Select
AlreadyRun = True
End If
End Sub
which will only do something the first time the sheet is activated (as the AlreadyRun variable will originally be False, but will be changed to True once it is run once), or
Private AlreadyRun As Boolean
Private Sub Worksheet_Activate()
Dim CurRng as Range
Set CurRng = Selection
Columns("A:P").Select
ActiveWindow.Zoom = True
CurRng.Select
If Not AlreadyRun Then
Range("A1").Select
AlreadyRun = True
End If
End Sub
which will resize the sheet every time it is activated, but only move the selected cell to A1 the first time.
To avoid the issue caused by the sheet which is current when the Workbook is saved not going through the Worksheet_Activate event when the workbook is reopened, you can include a Workbook_Open event that says
Private Sub Workbook_Open()
Application.Screenupdating = False
Dim ws As Worksheet
Set ws = Activesheet
'For the next two lines, just pick any two of your worksheets
'All it is trying to do is to ensure whichever sheet was active at open
'is deactivated before being activated again in the "ws.Activate" command
Worksheets("Sheet1").Activate
Worksheets("Sheet2").Activate
ws.Activate
Application.Screenupdating = True
End Sub
(Disabling Screenupdating while the event is run will avoid the users seeing any "flickering" of worksheets.)

Related

Excel VBA: protecting my worksheets slows down my vba code significantly

I am very new to VBA and have basically taught myself while building my current Excel 'contract'. My goal is have a list of contract options which are shown or hidden depending on their representative check boxes. There are 12 total options with ranges that I show/remove across 4 worksheets.
In terms of organization, I have utilized modules based on each action. I also named all my ranges
Prior to me protecting my worksheet, when I select a checkbox, all 4 ranges across all 4 worksheets immediately show. When I unselect, they immediately clear their contents and hide. Yay!
Once I protect my worksheet, however, things either slow down to a crawl or I get an error. In my ProtectWorksheet module below, the commented out lines work, but from reading other stack overflow articles it seens better to use the code I have. Unprotected, it works great. Protected I get the "Error 1004': Unable to set the Hidden property of the Range class". If I instead use my commented out code while protected, it works but is super slow.
Technically I can get everything to work...but from a user interface stance it's terrible.
Below is the 1st contract option I have been testing. Please and thank you for any and all help!
under the Excel Objects - sheet2(Data Input)
Private Sub chkDomesticHotWater_Click()
ProtectOFF
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
If chkDomesticHotWater = True Then
AddDomesticHotWater
Else
'Remove the lines, clear the data, and move the mouse to the top
RemoveDomesticHotWater
ClearDomesticHotWater
Range("A1").Select
End If
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
ProtectON
End Sub
under the Module: Checkboxes
Sub AddDomesticHotWater()
[DataInput_DomesticHotWater].EntireRow.Hidden = False
[Contract_DomesticHotWater].EntireRow.Hidden = False
[Invoice_DomesticHotWater].EntireRow.Hidden = False
[ExpectedCost_DomesticHotWater].EntireRow.Hidden = False
End Sub
Sub RemoveDomesticHotWater()
[DataInput_DomesticHotWater].EntireRow.Hidden = True
[Contract_DomesticHotWater].EntireRow.Hidden = True
[Invoice_DomesticHotWater].EntireRow.Hidden = True
[ExpectedCost_DomesticHotWater].EntireRow.Hidden = True
End Sub
Under the Module ClearData
Sub ClearDomesticHotWater()
Range("DataInput_DomesticHotWater").Select
For Each cell In Selection
If cell.Interior.Color = RGB(226, 239, 218) Then
cell.ClearContents
End If
Next
Range("DomesticHotWaterStart").Select
End Sub
under the Module ProtectWorksheet
Sub ProtectON()
Dim ws As Worksheet
Dim pwd As String
pwd = "123" ' Put your password here
For Each ws In Worksheets
ws.Protect Password:=pwd, UserInterfaceOnly:=True
Next ws
'Worksheets("Data Input").Protect Password:="123"
'Worksheets("Contract").Protect Password:="123"
'Worksheets("Invoice").Protect Password:="123"
'Worksheets("Expected Cost").Protect Password:="123"
End Sub
Sub ProtectOFF()
Dim ws As Worksheet
Dim pwd As String
pwd = "123" ' Put your password here
For Each ws In Worksheets
ws.Unprotect Password:=pwd
Next ws
'Worksheets("Data Input").Unprotect Password:="123"
'Worksheets("Contract").Unprotect Password:="123"
'Worksheets("Invoice").Unprotect Password:="123"
'Worksheets("Expected Cost").Unprotect Password:="123"
End Sub
EDIT
I was able to speed it up just a tiny bit by updating my Protect On/Off code below, but it's still a 3-5 second delay when I click on my check boxes:
Sub ProtectON()
Dim ws As Worksheet
Set WSArray = Sheets(Array("Data Input", "Contract", "Invoice", "Expected Cost"))
For Each ws In WSArray
ws.Protect Password:="123"
Next
End Sub
Sub ProtectOFF()
Dim ws As Worksheet
Set WSArray = Sheets(Array("Data Input", "Contract", "Invoice", "Expected Cost"))
For Each ws In WSArray
ws.Unprotect Password:="123"
Next
End Sub
EDIT - SOLUTION?
So I don't think this is best practice, nor have I really 'solved' my delay, but I found a workaround. I eliminated the delay when clicking my check boxes by turning on protection yet allowing row formatting. Technically my sheet is no longer 100% protected from user tinkering, but I think that risk is worth removing such an annoying wait time after clicking.
Sub ProtectON()
Dim ws As Worksheet
Set WSArray = Sheets(Array("Data Input", "Contract", "Invoice", "Expected Cost"))
For Each ws In WSArray
ws.Protect Password:="123", AllowFormattingRows:=True
Next
End Sub
It should not be that slow, although I really have no clue how fast is your PC and how big is the data. However, here is something you can make better:
Sub ClearDomesticHotWater()
For Each cell In [DataInput_DomesticHotWater]
If cell.Interior.Color = RGB(226, 239, 218) Then
cell.ClearContents
End If
Next
End Sub
and remove all selects, they are slowing you down. Go around them like this:
How to avoid using Select in Excel VBA macros

Use cell value as range to hide columns

I have a spreadsheet that there is a checkbox the purpose of the checkbox is to hide the name of clients in two adjacent columns. Because the spreadsheet changes from time to time the position of the columns changes thus it is currently P:Q but a year ago it was H:I.
I want to store the 'range' in a cell and reference that from my vba and get that to hide the columns. The checkbox is a simple toggle. I have tried various incarnations without success and my latest effort tells me that I have not se up the range properly. The cel I am using for teh range is F4. The code is currently:
Private Sub CheckBox2_Click()
Dim c As Range
Dim Visy As Integer
Dim My_range As String
'My_range is the range of filled rows stored as a range in cell F4
'Visy stores the state of the checkbox
If CheckBox2.Value = True Then
Visy = 1
Else
Visy = 0
End If
'Stop any use of the spread sheet and set variable initial states
Application.EnableEvents = False
My_range = Sheet9.Cells(4, 6).Value
'Hide the columns
Range(My_range).Hidden = Visy
'Sheet9.colums(My_range).Hidden = True
'Re enable application
On Error GoTo 0
Application.EnableEvents = True
End Sub
This is within a single sheet:
Sub qwerty()
My_range = Cells(4, 6).Value
Range(My_range).EntireColumn.Hidden = True
End Sub
Your Private Sub CheckBox2_Click should be in a worksheet's code sheet. I believe this is the worksheet identified by the Sheet9 worksheet .CodeName property.
A Private Sub in a worksheet codesheet does not have to explicitly reference the .Parent worksheet property on any Range object or
Range.Cells object unless you want to reference another worksheet's cells. These are bound to the cells on the worksheet whose codesheet you are on regardless of the ActiveSheet property.
Private Sub CheckBox2_Click()
Range(Cells(4, "F").Text).EntireColumn.Hidden = CBool(Me.Value)
End Sub
Do not confuse a worksheet's Private Sub behavior with a Private Sub on a module code sheet. A module codesheet should always explicitly reference the parent worksheet (and often the parent workbook) regardless of whether the Sub is Public or Private.
You have to use code in context:
Private Sub CheckBox2_Click()
Dim wsh As Worksheet
Dim sRangeName As String
'context!
Set wsh = ThisWorkbook.Worksheets("TypeNameHere")
sRangeName = wsh.Range("F4")
wsh.Range(sRangeName).EntireColumn.Hidden = CheckBox2.Value
Set wsh = Nothing
End Sub
Thanks to all who responded it helped a lot and put me on the right track. As several of you noted context is important and I was mixing private sub and sub and so had a scope problem when it came to ranges. I also from another source had the suggestion to use a named range rather than read a cell value since the columns were always adjacent. I have published the code below in case it is of value to anyone in the future.
Private Sub CheckBox2_Click()
'Requires ClientNameCol to be set to the range to be hidden
Dim Visy As Boolean
'Stop any use of the spread sheet and set variable initial states
Application.EnableEvents = False
'Check if sheet is to be hidden or not
If Worksheets("Client 16").CheckBox2.Value = True Then
Visy = True
Else
Visy = False
End If
'Hide/unhide the columns
With ThisWorkbook
.Worksheets("Client 16").Range("ClientNameCol").EntireColumn.Hidden = Visy
End With
On Error GoTo 0
Application.EnableEvents = True
End Sub

Run a Macro every time sheet is changed

i'm still fairly new to macros, i've got a bit of code i need to run on a sheet every time it gets updated, changed, or whatever.
Here is the code I need to run: How can i do this?
Sub UnMergeFill()
Dim cell As Range, joinedCells As Range
For Each cell In ThisWorkbook.ActiveSheet.UsedRange
If cell.MergeCells Then
Set joinedCells = cell.MergeArea
cell.MergeCells = False
joinedCells.Value = cell.Value
End If
Next
End Sub
You can boost the efficiency of your macro by locating the merged cells to process rather than looping through every cell in the Worksheet.UsedRange property and examining it for the Range.MergeCells Property.
Within the worksheet's conventional Range.Find method, there is an option to look for formatting. On this sub-dialog's Alignment tab, you'll find the option to locate Merged cells.
        
This can be incorporated into your VBA sub procedure using the Range.Find method and the Application object's .FindFormat property.
Your sub procedure using FindFormat:
Sub UnMergeFill(Optional ws As Worksheet)
If ws Is Nothing Then Set ws = ActiveSheet
Dim fndMrg As Range, joinedCells As Range
Application.FindFormat.MergeCells = True
With ws
On Error Resume Next
Set fndMrg = .Cells.Find(What:=vbNullString, SearchFormat:=True)
Do While Not fndMrg Is Nothing
Set joinedCells = fndMrg.MergeArea
fndMrg.MergeCells = False
'fndMrg.UnMerge '???
joinedCells.Value = fndMrg.Value
Set fndMrg = .Cells.Find(What:=vbNullString, SearchFormat:=True)
Loop
End With
Application.FindFormat.MergeCells = False
End Sub
Slightly revised Worksheet_Change event macro with more environment shutdown during processing.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo bm_Safe_Exit
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
Call UnMergeFill(Target.Parent)
bm_Safe_Exit:
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
I've opted to specify the worksheet to be processed rather than rely on the ActiveSheet property. There is the possibility that the Worksheet_Change could be initiated by an outside process when it is NOT the active sheet.
In short, opt for bulk operations whenever possible and avoid looping whenever you can. This is not blinding fast but it should be substantially quicker than looping through the cells.
In the code module for that particular worksheet, just add this:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
UnMergeFill
Application.EnableEvents = True
End Sub

Showing hidden column in another sheet

I am having trouble finding how to "show" a hidden column in another sheet with VBA.I am currently studying VBA and I wanted to have a hide/unhide code for every case, but this one is missing. Any suggestions?
My (updated) code is here:
Private Sub CommandButton1_Click()
'To Hide Sheet 2
Worksheets("Sheet2").Visible = False
'To Hide Rows 22 to 25
Rows("22:25").EntireRow.Hidden = True
'To Hide Columns E to G
Columns(":G").EntireColumn.Hidden = True
'More specific hidding (inside a different sheet)
Worksheets("Sheet3").Columns("A:G").EntireColumn.Hidden = True
End Sub
Public Sub UnHideAll()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
ws.Visible = xlSheetVisible
Rows.Hidden = False
Columns.Hidden = False
Next ws
End Sub
Private Sub CommandButton2_Click()
UnHideAll
End Sub
Try
Sub UnHideAll()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
ws.Visible = xlSheetVisible
ws.Rows.Hidden = False
ws.Columns.Hidden = False
Next ws
End Sub
The point of the code is that you need to qualify Rows and Columns by the worksheet if you want them to refer to anything other than the active sheet. Prefixing them by ws. lets VBA know what sheet the rows and columns are on. Then in the code for the button just:
Private Sub CommandButton1_Click()
UnHideAll
End Sub
I've tested it a number of times using both manually columns, rows, and sheets, as well as when it was VBA doing the hiding, and it seems to work fine.

how can I have every excel workbook I open to have columns.autofit property?

I would like to write a macro or adjust Excel properties, so that whenever I open a new excel workbook all values are autofited to the columns.
I tried using the following code, which works if I save the code for a specific workbook, however if I save the code in the PERSONAL workbook, the code produces an error. "Method 'Columns' of object '_Global' failed"
Sub auto_open()
Columns().AutoFit
End Sub
The way I do this is to create a simple add-in that handles Application events I want to intercept. The reason that it doesn't work in the auto_open() is because you are trying to work with the Columns object before it gets instantiated. Much better to use the SheetActivate event. This also avoids the possibility of opening a Workbook with 20 pages and having to wait for all of them to AutoFit. You only see the active sheet, right?
The concept is to grab a reference WithEvents to the application and set up handlers to whatever events you care about. To do this, you'll have to put the code into a class. I called mine "AppHolder".
Class code:
Option Explicit
Private WithEvents app As Application
Private Sub Class_Initialize()
Set app = Application
End Sub
Private Sub app_SheetActivate(ByVal Sh As Object)
Sh.Columns().AutoFit
End Sub
Private Sub app_WorkbookActivate(ByVal Wb As Workbook)
Wb.ActiveSheet.Columns().AutoFit
End Sub
Private Sub app_WorkbookNewSheet(ByVal Wb As Workbook, ByVal Sh As Object)
Sh.Columns().AutoFit
End Sub
Then, create an instance of your class in and set it in auto_open (or Workbook_Open as the case may be) in the ThisWorkbook module:
Option Explicit
Private hook As AppHolder
Private Sub Workbook_Open()
Set hook = New AppHolder
End Sub
Save it as an Excel add-in file (.xlam) in the default location - should be in Users[You]\AppData\Roaming\Microsoft\AddIns. Close Excel and re-open it, then go to Developer...Add-Ins and enable it. All there is to it.
EDIT: Almost forgot - that doesn't cover all situations in which you'll be presented with a Worksheet. You need WorkbookActivate and WorkbookNewSheet too...
You could use something like this: (untested)
Sub auto_open()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.EnableEvents = False
Application.DisplayAlerts = False
Dim wb As Workbook
Dim ws As Worksheet
For Each wb In Workbooks
For Each ws In wb.Worksheets
ws.Columns.AutoFit
Next ws
Next wb
Application.DisplayAlerts = True
Application.Calculation = xlCalculationAutomatic
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Its important to identify the Objects you are working on (in this case workbooks and worksheets) because it helps you to know which method you could apply to them (see)
This might work :
Sub auto_open()
For i = 1 To Application.Workbooks.Count
For j = 1 To Application.Workbooks(i).Sheets.Count
For k = 1 To Application.Workbooks(i).Sheets(j).Cells(1, Columns.Count).End(xlToLeft).Column
Application.Workbooks(i).Sheets(j).Columns(k).EntireColumn.AutoFit
Next k
Next j
Application.Workbooks(i).Save
Next i
End Sub