I currently have a workbook that contains several 'veryhidden' sheets, which only will become visible when the correct password has been entered. However, in the case that the correct password is entered, how do I write a code which activates that sheet that just has become visible, i.e. Excel needs to ignore all the 'veryhidden' ones.
I have tried the code:
Sub MoveNext()
On Error Resume Next
Sheets(ActiveSheet.Index + 1).Activate
If Err.Number <> 0 Then Sheets(1).Activate
End Sub
However, this does not work.
Declare Global variables in separate module
say ,
Global Const g_sPassword As String = "password"
on the next module where you need to code ,do it as below
'mention only sheets that should be visible ,other sheets will be automatically hided
Sub OpenSheet(sSheetName As String)
Dim shtSheetToOpen As Worksheet, shtLoopSheet As Worksheet
Set shtSheetToOpen = ThisWorkbook.Sheets(sSheetName)
Application.ScreenUpdating = False
shtSheetToOpen.Visible = xlSheetVisible
For Each shtLoopSheet In ThisWorkbook.Sheets
If shtLoopSheet.Name <> shtSheetToOpen.Name And shtLoopSheet.Name <> "Sheets to be visible" Then
shtLoopSheet.Visible = xlVeryHidden
End If
Next shtLoopSheet
shtSheetToOpen.Activate
Set shtSheetToOpen = Nothing
Set shtLoopSheet = Nothing
End Sub
' then include the below code
Sub OpenSample()
Dim sUserInput As String
sUserInput = InputBox("Please enter password", "Password Prompt", "")
If sUserInput = g_sPassword Then
MsgBox "Access Granted", vbInformation, "Access"
Call OpenSheet("Sheetsname")
sheetname.Activate
Else
MsgBox "Incorrect Password.Please try again", vbCritical, "Error"
End If
End Sub
how do I write a code which activates that sheet that just has become visible
You can store the sheet that you are unhiding in an object and then activate
For example
Private ws As Worksheet
Sub UnhideCode()
'<~~ Just an example since I do not know how you are unhiding
If pass = "OK" Then
Set ws = Sheet1 '<~~ Change this to the relevant sheet
ws.Visible = xlSheetVisible
End If
End Sub
Sub MoveNext()
If Not ws is Nothing then ws.Activate
End Sub
Related
I am trying to test a cell for specific data. If it contains that data, I do not want my code to run (because that would leave my worksheet and workbook Unprotected). If the cell contains data that does not match the test specifics, then I want the code to run. My code is to unprotect the active workbook, then unprotect the active worksheet, then fill the value of cell N41 as the "sheet name", then protect the active worksheet, then protect the active workbook. I want to add the test to the top of the code to avoid security failures.
The data that I want to test the cell for is:
The cell does not contain more than 31 characters (including spaces between charaters)
The cell does not contain any of the following characters: \ / : ? * [ or ]
The cell is not blank (empty)
If any of the above data/characters are in the cell I want the code to not run and leave my password protection in place for both the protected worksheet and protected workbook.
If the cell contains less than 31 characters (including spaces), does not contain any of the unwanted characters, and has at least 1 character in it (not a blank cell) then I want the code to run. Any help would be greatly appreciated.
Private Sub CommandButton16_Click()
ThisWorkbook.Unprotect Password:="Password1"
ActiveSheet.Unprotect Password:="Password2"
ActiveSheet.Name = Range("N41").Value
ActiveSheet.Protect Password:="Password2"
ThisWorkbook.Protect Password:="Password1"
End Sub
I guess the real question is "How to check if some value is the correct name for a worksheet?" in order to minimize the period when the document is not protected, and to eliminate an error when renaming.
From the full list of naming conventions we can learn two additional rules. The name shouldn't be "History" and it shouldn't begin or end with an apostrophe '. Also, there shouldn't be other sheets with that name.
In my opinion, the easiest way to accomplish the main task is to wrap renaming with On Error statements.
Private Sub CommandButton_Click()
Const BookPass = "Password1"
Const SheetPass = "Password2"
Dim NewName as String
Dim ErrCode&, ErrDesc$, ErrMessage$
NewName = Range("N41").Value
With ThisWorkbook
.Unprotect BookPass
With ActiveSheet
.Unprotect SheetPass
On Error Resume Next
' ------ Main Part -------
.Name = NewName
' ------------------------
ErrCode = Err.Number
ErrDesc = Err.Description
On Error GoTo 0
.Protect SheetPass
End With
.Protect BookPass
End With
If ErrCode <> 0 Then
ErrMessage = "NewName=" & NewName & vbNewLine & _
"Error=" & ErrCode & vbNewLine & _
"Description: " & ErrDesc
MsgBox ErrMessage, vbCritical
End If
End Sub
p.s. I suppose, this code will be placed in the worksheet object module. In this case, it is better to replace ActiveSheet with Me for readability.
If you are prepared to weaken the Workbook protection, you can add use this code when protecting the Workbook.
Your code can then change the sheet name without unprotecting the WorkBook, but so can your users.
ActiveWorkbook.Protect Password:="Password1", Structure:=False
The WorkSheet can be protected to allow changes from your code but not by your users.
This way you protect the WorkSheet and never have to unprotect it.
ActiveSheet.Protect Password:="Password2", UserInterfaceOnly:=True
In your code, you can set a boolean value to true if a test passes and exit the sub with a custom message if a test fails. Then test the boolean value and if it is true, unprotect the Workbook, make the update and reprotect the Workbook.
Option Explicit
Private Sub ProtectAll()
ActiveWorkbook.Protect Password:="Password1"
' ActiveWorkbook.Protect Password:="Password1", Structure:=False
'Optional: Allow changes to sheet names and order, not ideal
'but allows you to not have to protect and unprotect the workbook
ActiveSheet.Protect Password:="Password2", UserInterfaceOnly:=True
'Allow changes to the active worksheet by VBA code, remains protected via the UI
End Sub
Private Sub UnprotectAll()
ActiveSheet.Unprotect Password:="Password2"
ThisWorkbook.Unprotect Password:="Password1"
End Sub
Private Sub ProtectWB()
ActiveWorkbook.Protect Password:="Password1"
End Sub
Private Sub UnprotectWB()
ThisWorkbook.Unprotect Password:="Password1"
End Sub
Private Sub Change()
Dim CellValue As String
Dim OKtoChange As Boolean
Dim ErrorMessage As String
CellValue = vbNullString
OKtoChange = False
CellValue = ActiveSheet.Range("N41").Value
If Len(CellValue) < 32 Then
OKtoChange = True
Else
ErrorMessage = "The WorkSheet name is more than 31 characters."
GoTo ErrorHandler
End If
'Other tests here to set the OKtoChange variable based on results
'If any test fails the code exits
If OKtoChange = True Then
Call UnprotectWB
ActiveSheet.Name = CellValue
Call ProtectWB
End If
Exit Sub
ErrorHandler:
MsgBox "Invalid name for the WorkSheet" & vbLf & ErrorMessage, vbCritical, "Invalid name"
End Sub
I've been trying to get the typical hide sheets/unhide sheets code to work for encouraging Macro enabling. Since I lock down saving, I needed to do it slightly differently than putting it in the Workbook_BeforeClose Sub as usually is done.
But...my hide sub
Sub HideSheets()
'Error Handling when workbook is unprotected.
On Error GoTo EH
'Unprotect the workbook to allow conditional formatting changes.
ThisWorkbook.Sheets("Field Service Report").Unprotect Password:="x"
ThisWorkbook.Sheets("Prompt").Unprotect Password:="x"
'Main Sub Code
Application.EnableCancelKey = xlDisabled
Sheets("Prompt").Visible = xlSheetVisible
Sheets("Field Service Report").Visible = xlSheetVeryHidden
Application.EnableCancelKey = xlInterrupt
'Reprotect worksheet before ending sub.
ThisWorkbook.Sheets("Field Service Report").Protect Password:="x", UserInterfaceOnly:=True
ThisWorkbook.Sheets("Prompt").Protect Password:="x"
Exit Sub
EH:
Call EH
Resume Next
End Sub
and my unhide sub
Sub UnhideSheets()
'Error Handling
On Error GoTo EH
'Unprotect the workbook to allow conditional formatting changes.
ThisWorkbook.Sheets("Field Service Report").Unprotect Password:="x"
ThisWorkbook.Sheets("Prompt").Unprotect Password:="x"
'Main Sub Code
Application.EnableCancelKey = xlDisabled
Sheets("Field Service Report").Visible = xlSheetVisible
Sheets("Prompt").Visible = xlSheetVeryHidden
Application.EnableCancelKey = xlInterrupt
'Reprotect worksheet before ending sub.
ThisWorkbook.Sheets("Field Service Report").Protect Password:="x", UserInterfaceOnly:=True
ThisWorkbook.Sheets("Prompt").Protect Password:="x"
Exit Sub
EH:
Call EH
Resume Next
End Sub
....works fine when called form the immediate window. Sheets hide and unhide appropriately.
But, when I step through the sub it doesn't actually do anything. The idea is to set the sheets to the "prompt" sheet before saving, save, then revert to usable after saving. But I can't even see if that code is working correctly (it appears to) because stepping through the actual hide/unhide subs doesn't do anything.
Edit: No errors, just doesn't change any settings to hide or unhide sheets.
Thoughts?
Edit: So given the comments below, my subs work when run from the immediate window and when stepped through via debugger. They hide and unhide the worksheets appropriately. So, the only thing that can be wrong is the code that calls these subs. So, here are two more subs. One is the button code for a save button, and the other is the Workbook_BeforeSave Sub.
Sub Save_Form()
'Error Handling ...
On Error GoTo EH
'Unprotect the workbook ...
ThisWorkbook.Sheets("Field Service Report").Unprotect Password:="x"
'Variable to disable any other save but this button.
Module1.SaveChk = 1
'Code to automatically save a copy ...
Module1.UserPath = Environ("USERPROFILE")
Module1.Path = UserPath & "\Desktop\"
If Module1.EditChk = "Y" Then
Module1.SaveName = "FSR Master"
Else
Module1.SaveName = Range("AF6").Value
End If
ThisWorkbook.SaveAs _
Filename:=Path & SaveName & ".xlsm", _
FileFormat:=52
If Module1.SaveError <> 1 Then
'User Display of Save Success
MsgBox "Filename = " & SaveName & vbNewLine _
& "File is saved to your desktop."
Else
Module1.SaveError = 0
End If
'Reset SaveChk variable
Module1.SaveChk = 0
'Reprotect Worksheet
ThisWorkbook.Sheets("Field Service Report").Protect Password:="x", UserInterfaceOnly:=True
Exit Sub
EH:
Call ErHa
Resume Next
End Sub
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
'Error Handling ...
On Error GoTo EH
'Save Initiation Check
If Module1.SaveChk <> 1 Then
Cancel = True
MsgBox "Please Use Form Save Button", vbOKCancel + vbExclamation, "SAVE CANCELLED"
Exit Sub
End If
If Module1.EditChk <> "Y" Then 'Skips the whole block if EditChk = Y
'Create the final range of cells for checking
Set Module1.Required = Application.Union(Module1.Fixed, Module1.Drive, Module1.Stage)
'Check if all required cells are filled in
If WorksheetFunction.CountA(Module1.Required) < Module1.Required.Count Then
Cancel = True
MsgBox "Please Completed Shaded Cells!", vbOK + vbExclamation, "SAVE CANCELLED"
Module1.SaveError = 1
Else
'Set the report date before saving
Application.EnableEvents = False
Range("AE59") = Format(Now(), "mm-dd-yyyy hh:mm:ss AM/PM")
Application.EnableEvents = True
End If
End If
'Renable Macro Splash Screen Before Save
Call HideSheets
Exit Sub
EH:
Call ErHa
Resume Next
End Sub
I have an excel workbook containing several worksheets.
What I want to do is have a mechanism like a user form or something where the user would authenticate to one of several possible users.
Based on the username supplied I want to display certain worksheets and hide other sheets, and block the user from accessing worksheets they should not be able to view.
Has anyone done something like this in Excel?
Any thoughts are appreciated
Sean
I actually enjoyed the task of typing this one up. Keep in mind, VBE is not protected in this code so you may want to add some protection, but this should do what you need.
You should also create a generic Login worksheet. This would be the only sheet open before a password is entered. This is essential as you are unable to hide every sheet without throwing an error. (You need to have 1 visible sheet).
WARNING: This code is mildly tested. You are responsible for any loss of data for using the below code, such as (but not limited to) forgetting a password. You have been warned!!!!
1. Open the Workbook, then make a Call to GetLogin
Option Explicit
Private Sub Workbook_Open()
GetLogin 1
End Sub
2. The Login Code
Private Sub GetLogin(ByVal AttemptNumber As Integer)
Dim Sheet As Worksheet
With ThisWorkbook.Worksheets("Login")
.Visible = xlSheetVisible
.Activate
End With
For Each Sheet In ThisWorkbook.Sheets
If Not Sheet.Name = "Login" Then
Sheet.Visible = xlSheetVeryHidden
End If
Next Sheet
Dim Password As String
Password = Application.InputBox("Please enter your password")
Select Case Password
Case "Ma$terPas$"
For Each Sheet In ThisWorkbook.Sheets
Sheet.Visible = xlSheetVisible
Next Sheet
ThisWorkbook.Worksheets(1).Activate 'For when you hide login sheet
Case "Oth3Rpa$$"
With ThisWorkbook
.Worksheets(1).Visible = xlSheetVisible
End With
ThisWorkbook.Worksheets(1).Activate 'For when you hide login sheet
Case Else
If AttemptNumber <= 3 Then
If MsgBox("You entered an incorrect password", vbRetryCancel, "Attempt # " & AttemptNumber) = vbRetry Then
AttemptNumber = AttemptNumber + 1
GetLogin AttemptNumber
Else
ThisWorkbook.Saved = True
ThisWorkbook.Close
End If
Else
ThisWorkbook.Saved = True
ThisWorkbook.Close
End If
End Select
ThisWorkbook.Worksheets("Login").Visible = xlSheetHidden
End Sub
3. Close the Workbook
Private Sub Workbook_BeforeClose(Cancel As Boolean)
If ThisWorkbook.Saved = False Then
If MsgBox("Would you like to save?", vbYesNo) = vbYes Then
ThisWorkbook.Save
End If
End If
Dim Sheet As Worksheet
With ThisWorkbook.Worksheets("Login")
.Visible = xlSheetVisible
.Activate
End With
For Each Sheet In ThisWorkbook.Sheets
If Not Sheet.Name = "Login" Then
Sheet.Visible = xlSheetVeryHidden
End If
Next Sheet
'Prevent from being asked to save the fact you just hid the sheets
ThisWorkbook.Saved = True
End Sub
Ensure that the Workbook_Open and Workbook_Close are in your Workbook's Module.
You could probably achieve this by using the Auto_Open event
Function Auto_Open()
Select Case True
Case InStr(Application.UserName, "Dan Smith") > 0
ActiveWorkbook.Sheets(1).Visible = xlSheetVeryHidden
Case InStr(Application.UserName, "Jon Doe") > 0
ActiveWorkbook.Sheets(1).Visible = True
End Select
End Function
Of course this would take a lot of work considering you'd have to find out everyone's usernames and then the sheets that you want to hide from them, but that's what I thought of
New here and I just started to teach myself coding. I have a workbook that has roughly 14 tabs/worksheets for employees to enter their hours worked per day. On a "Summary" tab and want to create a macro button for each employee to click on to view his/her tab. These employee tabs are hidden and all I want the action to do is unhide and then hide when the employee clicks their button.
Unfortunately, I receive an Ambiguous Error message and I created a module per employee. I assume I need to somehow "stack" code, but again am totally new to coding. Below is a sample of my code
Private Sub ShowHideWorksheets()
Sheets("EMPLOYEE 1").Visible = Not Sheets("EMPLOYEE 1").Visible
End Sub
you need to correctly put it behind a button. When you insert the button into the page, right click it and assign macro. The code would look like
Sub Button1_Click()
Sheets("EMPLOYEE 1").Visible = Not Sheets("EMPLOYEE 1").Visible
End Sub
Basically you wish to toggle visibility for a worksheet.
Assuming that you know which Sheet is going to be triggered, it is something like this:
Public Sub TriggerSheetVisibility(worksheetname as string)
Dim ws as WorkSheet
On Error Resume Next 'To avoid subscript out of range error if a worksheetname is passed that doesn't exit
Set ws = Worksheets(worksheetname)
On Error Goto 0
If Not ws Is Nothing Then 'Only when the worksheet exists, we can execute the rest of this sub:
If ws.Visible = True then
ws.Visible = False
Else
ws.Visible = True
End If
End If
End Sub
Also see https://msdn.microsoft.com/en-us/library/office/ff197786.aspx
This is also an acceptable approach? Prolly long winded though
Private Sub CommandButton1_Click()
Dim sheet As Worksheet
For Each sheet In ActiveWorkbook.Sheets
If sheet.Name <> CommandButton1.Caption Then
sheet.Visible = False
End If
If sheet.Name = CommandButton1.Caption Then
sheet.Visible = True
End If
Next sheet
End Sub
However I like this better due to the fact you only need one button
Private Sub CommandButton1_Click()
Dim sheet As Worksheet
For Each sheet In ActiveWorkbook.Sheets
If sheet.Name <> Environ("USERNAME") Then
sheet.Visible = False
End If
If sheet.Name = Environ("USERNAME") Then
sheet.Visible = True
End If
Next sheet
End Sub
If you change Private to Public it should work. I'm presuming you're just creating macros at this point to get base functionality to work. You can hide (as the code you've posted) and unhide like this:
' This first macro actually just makes the worksheet visible and then
' invisible each time you execute it - so I'm not sure if
' that's what you're after
Public Sub ShowHideWorksheets()
Sheets("EMPLOYEE 1").Visible = Not Sheets("EMPLOYEE 1").Visible
End Sub
' If it's invisible you can do this.
Public Sub ShowWorksheets()
Sheets("EMPLOYEE 1").Visible = True
End Sub
' Basically that should give you an idea of how to proceed.
Why would an input box stop accepting a selection by mouse after a call to a sub with screenupdating variable changes?
I have a large workbook in excel that calculates a budget from different components on different sheets. I'm using named ranges in all of my formulas, and as I build the workbook I often need to move things around on the sheet, and thus edit the references to my named ranges so I made a macro to run through my named ranges and let me click to update their references.
I've included three subs from my workbook code; sheet 1 just has some values in the named range cells, a formula ( = CNGFixedCost1 + CNGFixedCost2 + CNGFixedCost3), and an activex check box. When I run RangeNameManager() the inputbox stops accepting mouse selections, due to the screenupdating variable in the Worksheet_Calculate() sub, . I figured out how to resolve the problem while writing this up (remove the screenupdating changes), but I'm still curious as to why this happens.
Option Explicit
'Name Ranges in workbook
Public Sub Workbook_Open()
Worksheets("Sheet1").Range("D3").Name = "CNGFixedCost1"
Worksheets("Sheet1").Range("D4").Name = "CNGFixedCost2"
Worksheets("Sheet1").Range("D5").Name = "CNGFixedCost3"
End Sub
'Update named ranges
Sub RangeNameManager()
Dim nm As Name
Dim nms As String
Dim xTitleID As String
Dim InputRng As Range
Dim asnms As String
On Error Resume Next
asnms = CStr(ActiveSheet.Name)
For Each nm In ActiveWorkbook.Names
nms = CStr(nm.Name)
If nm.RefersTo Like "*" & asnms & "*" Then
Set InputRng = ActiveSheet.Range("A1")
Set InputRng = Application.InputBox("The current range for" & nms & " is " & CStr(nm.RefersTo) & ". Select the new range.", InputRng.Address, Type:=8)
nm.RefersTo = InputRng
End If
Next
On Error GoTo 0
End Sub
' Update check box automatically
Private Sub Worksheet_Calculate()
Application.ScreenUpdating = False '***Removed to resolve problem.***
Dim errwksht As String
errwksht = ActiveSheet.Name
On Error GoTo ErrorHandler
If Worksheets("Sheet1").Range("CNGFixedCost1").Value > 0 Then
Worksheets("Sheet1").CheckBox1.Value = False
Else
Worksheets("Sheet1").CheckBox1.Value = True
End If
ErrorHandler:
Exit Sub
Application.ScreenUpdating = True '***Removed to resolve problem.***
End Sub
ScreenUpdating is a property of the Application object. If you turn it to false, then the application cuts off connection with the user (it won't take input, and it won't update the display).
It's very useful if you want to make something run faster, however it shouldn't be used during times when you need user interaction.
You're exiting the sub before turning screen updating back on, leaving the application in an unstable state.
' Update check box automatically
Private Sub Worksheet_Calculate()
Application.ScreenUpdating = False '***Removed to resolve problem.***
'...
ErrorHandler:
Exit Sub 'exits here
Application.ScreenUpdating = True ' so this NEVER executes
End Sub
This is easily fixed by resuming at your error handler, which would be better named CleanExit:. Here's how I would write it.
Private Sub Worksheet_Calculate()
On Error GoTo ErrorHandler
Application.ScreenUpdating = False '***Removed to resolve problem.***
'...
CleanExit:
Application.ScreenUpdating = True
Exit Sub
ErrorHandler:
' Actually do some error handling
Resume CleanExit
End Sub