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.
Related
After doing a File > Protect Workbook > Protect Current Sheet > Unselect All > Okay
Anytime I press a keydown while viewing the sheet I get a
"The cell or chart you're trying to change is on a protected sheet." dialog.
I'd like to disable this.
I currently have a Auto_Open macro which does a Application.DisplayAlerts = False
This does not seem to help, even though I'm sure the macro is running on startup.
Any ideas?
So here is a workaround by kskinne:
1) Make sure all cells you don't want changed are locked Format Cells > Locked
2) Then instead of protecting worksheet use the following code in the code pane for the sheet of interest.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Locked = True Then Application.Undo
Application.EnableEvents = True
End Sub
I like this solution. I don't think switching-off DisplayAlerts will work. I put
Application.DisplayAlerts = False into Worksheet_Activate event and the message still fired.
I know, I am asking an unusual question. But, please do help me.
I have a below code on Workbook that will take care of copy/paste data on sheets. It would allow me to paste data into the cells without changing format(past only values).
Basically, the code will use destination formatting. similar to "paste values". It would allow the user to paste data from any other format. So that format is consistent across sheets.
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim vNewValues as Variant
NewValues = Target
Application.EnableEvents = False
Application.Undo
Target = NewValues
Application.EnableEvents = True
End Sub
Along with above code, I also have another code on the sheet that will help me to clear the contents and code is linked to a button. So, when the button is pressed it will clear the contents of the sheet.
Private Sub ResetKey_Click()
If MsgBox("Content of the sheet will be deleted and cannot be restored", vbOKCancel + vbInformation) = vbOK Then
Worksheets("User").Range("E19:I3018").ClearContents
Else
Exit Sub
End If
End Sub
Concern: I see a conflict between these codes. Because, when I click on the button I get the error that will point me to Application.Undo in the first code. I tried debugging the code but I was not able to get both to work. Please Suggest.
This will work:
Private Sub ResetKey_Click()
If MsgBox("Content of the sheet will be deleted and cannot be restored", vbOKCancel + vbInformation) = vbOK Then
Application.EnableEvents = False
Worksheets("User").Range("E19:I3018").ClearContents
Application.EnableEvents = True
Else
Exit Sub
End If
End Sub
That is, you have to suppress the Change event in other macros working on that sheet. Not elegant but doable.
To clarify what the first macro does: it saves the cell's content, undoes a user's paste or input, and then only fills in the value which was pasted, leaving the format intact. The problem with this approach is that the event handler does not return information on the action that triggered it - it could be a paste but clearing cells as well.
You can only use .Undo to undo the last action in the worksheet not to undo vba actions and must be the first line in the macro. As explained in the documentation.Application.Undo. Quote below:
This method undoes only the last action taken by the user before
running the macro, and it must be the first line in the macro. It
cannot be used to undo Visual Basic commands.
I'm trying to write a WorkBook_BeforeClose event in the ThisWorkbook module of my workbook that removes all validation dropdowns and resets all filters on every page.
When I try to close the workbook, this code runs:
Sub Workbook_BeforeClose(Cancel As Boolean)
Dim ws As Worksheet
For Each ws In Worksheets
.SpecialCells(xlCellTypeAllValidation).Delete
.ShowAllData
Next
End Sub
and I get a 'Compile error: Invalid or unqualified reference' with ".SpecialCells" highlighted. If I comment out that line, I get the same error with '.ShowAllData' highlighted.
I've checked the spelling and syntax against multiple web references, and it all seems valid. What have I done wrong or omitted?
1- You don't have a With bloc so you cannot use a dot . without explicit qualification to an object
2- SpecialCells is not a member of Worksheet but of Range, so you need to call it on the Cells object of the worksheet.
3- to remove the validation but keep the cells' values, you need to delete the validation but not the range itself
4- check the AutofilterMode prior to removing the filtering, and use .autofilter with no arguments to remove it.
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
If ws.AutoFilterMode Then ws.Cells.AutoFilter ' <-- remove autofilters
ws.Cells.Validation.Delete ' <-- removes validation, not the content
Next
End Sub
5- Finally, the Workbook_BeforeClose event might not be the appropriate event for your task, because the changes (validation and autofilters removed) will not necessarily be saved. You might consider using instead the Workbook_BeforeSave event.
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.
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