Editing Protected Worksheet From Another VBA - vba

I am working on a sign in book using Excel, coding with VBA. I have one tab with the form to type your information, another for the details to be stored upon login, and another to search for a logged user. I have protected Sheet1(Login), and need Sheet2(Log Book) to be protected as well.
The Issue The issue is that on Sheet2(Log Book) I want the VBA to be able to add the user's information in the next available row, while having it protected so nobody can go through and change others information. The following is the failed code which I have tried:
Private Sub Worksheet_Open()
Worksheet.Protect "Password", UserInterfaceOnly:=True
End Sub
wksht unportect()/wksht protect()
Neither of these were working. How else might I achieve this?

You need to define which worksheet you want to protect. In the example below I have set the object variable WrkSht to contain the worksheet properties of the sheet "Log Book". You can protect the sheet by using the protect-method on this object variable.
Sub ProtectWorksheet()
Dim WrkSht As Worksheet
Set WrkSht = ActiveWorkbook.Sheets("Log Book")
WrkSht.Protect Password:="password", UserInterfaceOnly:=True
End Sub

Related

Excel VBA - How to use worksheet event in add-in module?

I am new to Excel Add-ins and I am not sure how to write mi programm.
I would like to put in an add-in a code so that, when the workbook that uses the add-in is opened, it creates a sheet named "mainSheet".
I can use the event handler in the Workbook, but is it possible to put the code in the module of the add-in and still be able to run it?
I found this on the "Automate Excel" web site. Hope this helps
The following code works opening a workbook. It automatically adds a new sheet and labels it with the name. It also checks to see that the sheet doesn’t already exist – to allow for the possibility of it being opened more than once a day.
This code makes use of the Workbook Open Event and must be placed in the workbook module under the “Open work Book” event. The function Sheet_Exists must be placed in a module and this checks whether or not the sheet exists:
Private Sub Workbook_Open()
Dim New_Sheet_Name As String
New_Sheet_Name = "mainSheet"
If Sheet_Exists(New_Sheet_Name) = False Then
With Workbook
Worksheets.Add().Name = New_Sheet_Name
End With
End If
End Sub
==
Function Sheet_Exists(WorkSheet_Name As String) As Boolean
Dim Work_sheet As Worksheet
Sheet_Exists = False
For Each Work_sheet In ThisWorkbook.Worksheets
If Work_sheet.Name = WorkSheet_Name Then
Sheet_Exists = True
End If
Next
End Function

excel vba how to reference a macro created worksheet by codename in subsequent running

I'm very new at VBA ad I have the following problem.
I want to reference worksheets by codenames (because the tab name can be modify by the user)
I know that is not possible add a new worksheet specifying the codename.
In a running of my macro I create a new Worksheet using:
Worksheets.Add().Name = "aSheet"
st = Worksheets("aSheet").CodeName
now I have the codename in variable st.
In a following run of the macro (in one in which I don't create the new worksheets) I want to access the previous created worksheet by codename i.e. I
want to use code with codename hard coded. I don't want to use
st = Worksheets("aSheet").CodeName
because between the two runs of the macro the user must have changed the tab "aSheet" name.
That seems impossible to me, but I hope to be wrong.
Instead of going to the workbook's Worksheets collection, just refer directly to the worksheet by its codename:
debug.print Sheet5.name
You can also use it's index if you are super into the Worksheets collection:
debug.print Sheets(5).name
You might find it helpful to save the worksheet as a global variable (declared outside the scope of your function or subroutine. The global variable will be available after code execution, but will be reset if the workbook is closed and reopened.
Dim st As Worksheet
Sub addWorksheet()
Set st = ThisWorkbook.Worksheets.Add()
st.Name = "test3"
Debug.Print st.Name, st.CodeName
End Sub
That variable st is a worksheet object and can be referenced in any other subroutine or function after it's set.
...later on
Sub printWSName()
Debug.print st.name
End Sub
Finally you may want to save this value if the workbook closes. In your Workbook Object in the Project pane you can use the Workbook_BeforeClose and Workbook_Open events to save and recapture this value:
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Worksheets("savedStuff").Cells(1, 1).Value = st.Name
End Sub
Private Sub Workbook_Open()
Set st = Worksheets(st.Name)
End Sub

VBA Excel: Moving data macro error on protected sheets

I have the following example code in a module of VBA:
Sub My_Code()
ThisWorkbook.Sheets("Main").Range("A1") = "Main Data"
ThisWorkbook.Sheets("Secondary").Range("A2").Copy Sheets("Main").Range("B2")
End Sub
and to protect the sheets, Main and Secondary, I have put the following code in Thisworkbook of VBA:
Private Sub Workbook_Open()
Sheets("Main").Protect Password:="Mypassword", UserInterfaceOnly:=True
Sheets("Secondary").Protect Password:="Mypassword", UserInterfaceOnly:=True
End Sub
When I run My_Code() I get the error:
""Run-time error '1004'
The cell or chart you're trying to change is on a protected sheet.
To make changes, click Unprotect Sheet in the Review tab (you might need a password).""
And this debugs to the ThisWorkbook.Sheets("Secondary").... line.
When I manually Unprotect the Main sheet the code runs. Any ideas why I can't leave Main protected? Have I forgotten something?
#jkpieterse Gave the solution to this question which was to change the second line of the My_Code() to
Thisworkbook.Sheets("Main").Range("B2").Value = ThisWorkbook.Sheets("Secondary").Range("A2").Value
However this created a new error in my code which is mentioned in the comments about. The who reason behind this problem is that the UserInterfaceOnly = true does not allow macros to modify the sheet, it only allows for value changes. Therefore there is no way to use the interface protect work around when you modify the worksheet. The only solution from here is:
Sub My_Code()
Dim ws as Worksheet
Set ws = ThisWorkbook.Sheets("Main")
ws.UnProtect Password:="Mypassword"
On Error GoTo ErrHandeler
ws.Range("A1") = "Main Data"
ThisWorkbook.Sheets("Secondary").Range("A2").Copy ws.Range("B2")
ws.Protect:="Mypassword"
ErrHandler:
ws.Protect:="Mypassword"
End Sub
This is the next most secure solution.
The cells that you want to populate data in or modify needs to be unlocked. So select the range of cells, then in format cell set them to be unlocked. When you password protect the sheet, make sure you check allow to edit unlocked cells.
Try this with you original code, it should work. Since it worked when you unprotected the sheet.
It appears you have to reset the protection with UserInterfaceOnly = True upon Workbook_Open, which resolves my issue. Excel closes the workbook with a default of UserInterfaceOnly = False (even though I run the secure commands on the Workbook_BeforeClose event.

VBA un-protect sheet, run sub, then re-protect sheet?

Here is my issue, I have subs that work when I tested them with the sheet unlocked, but when I locked the sheet to protect certain cells from being selected or deleted/altered, the subs error out. So I need to add a part to my sub that unlocks, runs the main code, then re-locks the sheet.
I am looking for something like this
Sub Example ()
Dim sample as range
set sample as range("A3:Z100")
Application.ScreenUpdating = false
UN-PROTECT CODE
'Existing sub code here
RE-PROTECT CODE
Application.ScreenUpdating = True
End Sub
I am however unaware on what the code to achieve this should look like. I have tried researching and all I found was incomplete code that based on the comments, didn't work all the time. I did find a suggestion to upon error, have an error handler re-protect the sheet, but not sure how to write this either. Any suggestions?
Oh, and the people who will be using this sheet will not have access to the sheet password. I plan to have the module its self password protected and the subs attached to buttons. So placing the Sheet unlock password in the sub would be ok if it is needed.
Posting my original comment as an answer.
If you use the macro recorder and then protect & unprotect sheets, it will show you the code.
EDIT: Added the below.
If you attempt to unprotect a sheet that is not protected you will get an error. I use this function to test if a sheet is protected, store the result in a Boolean variable and then test the variable to see if a) the sheet must be unprotected before writing to it and b) to see if the sheet should be protected at the end of the proc.
Public Function SheetIsProtected(sheetToCheck As Worksheet) As Boolean
SheetIsProtected = sheetToCheck.ProtectContents
End Function
Do you need it to remove passwords? This worked for me
Sub macroProtect1()
Sheet1.Unprotect Password:="abc"
'Enable error-handling routine for any run-time error
On Error GoTo ErrHandler
'this code will run irrespective of an error or Error Handler
Sheet1.Cells(1, 1) = UCase("hello")
'this code will give a run-time error, because of division by zero. The worksheet will remain unprotected in the absence of an Error Handler.
Sheet1.Cells(2, 1) = 5 / 0
'this code will not run, because on encountering the above error, you go directly to the Error Handler
Sheet1.Cells(3, 1) = Application.Max(24, 112, 66, 4)
Sheet1.Protect Password:="abc"
ErrHandler:
Sheet1.Protect Password:="abc"
End Sub
had a similar problem and found this code on the web:
Sub protectAll()
Dim myCount
Dim i
myCount = Application.Sheets.Count
Sheets(1).Select
For i = 1 To myCount
ActiveSheet.Protect "password", true, true
If i = myCount Then
End
End If
ActiveSheet.Next.Select
Next i
End Sub
Sub Unprotect1()
Dim myCount
Dim i
myCount = Application.Sheets.Count
Sheets(1).Select
For i = 1 To myCount
ActiveSheet.Unprotect "password"
If i = myCount Then
End
End If
ActiveSheet.Next.Select
Next i
End Sub
Note that it is designed to protect / unprotect all sheets in the workbook, and works fine. Apologies, and respect, to the original author, I can't remember where I found it (But I don't claim it)...
The most common object that is Protected is the Worksheet Object This make it possible to preserve formulas by Locking the cells that contain them.
Sub Demo()
Dim sh As Worksheet
Set sh = ActiveSheet
sh.Unprotect
' DO YOUR THING
sh.Protect
End Sub
Here's my very simple technique for situations that don't require a password (which are most situations that I run into):
Dim IsProtected As Boolean
IsProtected = SomeWkSh.ProtectContents: If IsProtected Then SomeWkSh.Unprotect
'Do stuff on unprotected sheet...
If IsProtected Then SomeWkSh.Protect
You can, of course, simplify the syntax a bit by using a With SomeWkSh statement but if the "Do stuff..." part refers to properties for methods of a larger, spanning With statement object, then doing so will break that functionality.
Note also that the Protect method's Contents parameter defaults to True, so you don't have to explicitly specify that, although you can for clarity.

How to clear the VBA code of a worksheet via a macro?

I have a file where there's a template sheet that needs to run some code when it's activated. This sheet is being duplicated to create sheets that don't need to run this code. Currently, I have the code to check for worksheet's codename when run so that it does nothing on extra sheets, but it still slows usage down when you switch between sheets.
Is there any way to make the macro that makes duplicates also clear their VBA code contents?
(Edit) Please note that the code I need to clear is not in a module. After some research, it seems I found a way to remove modules (by accessing VBProject.VBComponents), but I'm not sure how to access the VBA code of a worksheet.
To remove complete code in all Sheet modules you could try something like this:
Sub Remove_some_vba_code()
Dim activeIDE As Object 'VBProject
Set activeIDE = ActiveWorkbook.VBProject
Dim Element As VBComponent
Dim LineCount As Integer
For Each Element In activeIDE.VBComponents
If Left(Element.Name, 5) = "Sheet" Then 'change name if necessary
LineCount = Element.CodeModule.CountOfLines
Element.CodeModule.DeleteLines 1, LineCount
End If
Next
End Sub
Another way you could approach this is to keep all of your code out of the worksheet. Then you don't have to delete anything. The worksheet's code module is a handy place to code events, but you can create your own class module to handle events too. Put this in a standard module:
Public gclsEvent As CEvent
Sub Auto_Open()
Set gclsEvent = New CEvent
Set gclsEvent.This = Sheet1
End Sub
This will create an instance of CEvent that's global, so it won't lose scope as long as your workbook is open. It assigns the worksheet codenamed Sheet1 to the This property of the class. Create a class module named CEvent with this code
Private WithEvents mwsThis As Worksheet
Public Property Set This(ByVal wsThis As Worksheet): Set mwsThis = wsThis: End Property
Public Property Get This() As Worksheet: Set This = mwsThis: End Property
Private Sub mwsThis_Activate()
Me.This.Copy , Me.This.Parent.Sheets(Me.This.Parent.Sheets.Count)
End Sub
The WithEvents keyword exposes events for that object. Since we're only hooking up the events for Sheet1, activating another sheet won't trigger the code.