I have some formulas set via vba to change the value in columns H, J, K, L and N. Those changes are based on G column value and a Submit button, this works fine.
When I do the process to lock them to avoid the user from editing, that says to unlock the whole sheet then lock the ones I need, after this I modify the G column and get:
"Autofit Method of Range Class Failed".
I use it on H column.
This get highlighted:
Sheet1.Range("H11:H50").Rows.EntireRow.AutoFit
You are trying to change protected cells using VBA while the protection is on. You can work around this various ways, however, the most simple solution is something like the following:
Sub Example()
Sheet1.Unprotect "YOURPASSWORD" 'if no password was used, you don't need to include it
Sheet1.Range("H11:H50").Rows.EntireRow.AutoFit
Sheet1.Protect "YOURPASSWORD"
End Sub
Alternative solution:
By using this in the on open procedure of your workbook. VBA can make changes on locked cells, but users can not.
One Caveat: if an error occurs in the code, this will reset and you need to close and reopen the worksheet in order for this to function again
Private Sub Workbook_Open()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
ws.Protect UserInterfaceOnly:=True
Next ws
End Sub
Related
I have a python script that edits contents of a certain column and VBA that checks to see if this column has changed. When it detects a change, it locks the column from user edits. It is successful in protecting the worksheet but the cells can still be edited. I can't understand why this is happening.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Protect As String
Dim NotProtectRange As String
ProtectRange = "A1:A1049576"
NotProtectRange = "B1:XFD1048576"
If Not Intersect(Target, Target.Worksheet.Range(ProtectRange)) Is Nothing Then
Worksheets("Sheet1").Unprotect
Range(NotProtectRange).Locked = False
Worksheets("Sheet1").Protect UserInterfaceOnly:=True, Contents:=True
End If
End Sub
I expect that when I double click the cells in the range ProtectRange, I should not be able to edit them. But instead, I can edit them. So how do I fix this?
If the cells are not locked then protecting the sheet does not prevent editing.
Me.Range(ProtectRange).Locked = True
should resolve the problem.
I've created a table for data entering. However, as user use it, they insert rows in the middle of the table. That messes the formula up as the functions were designed only work forward. Also sometimes when the user add row manually (just by typing into the next row after the last row of the table), the function were filled automatically but the function is incorrect quite often.
So I added a button to add the rows to the table and that works without problems. Now I want to disable the ability for user to add rows manually, meaning rows can ONLY be added via clicking the button.
As far as I research, people all suggesting using protect sheet functionality. But it would remove all ability to add rows including via VBA. Other offer the VBA that only prevent inserting rows via right click at the Rows Column. I need to disable all user-accessible ways.
This is the code for the button (if it's of any relevant).
Sub InsertRow_Click()
Dim i As Integer
For i = 1 To 10
ActiveSheet.ListObjects("Invoice").ListRows.Add alwaysinsert:=True
Next i
End Sub
When using sheet protection, you could add Userinterfaceonly= true, this will prevent user interference, but VBA code will still work.
Private Sub Workbook_Open()
Dim ws As Worksheet
For Each ws In ThisWorkbook.Worksheets
ws.Protect Password:="secret", UserInterFaceOnly:=True
Next ws
End Sub
or if you want to protect just one sheet:
Private Sub Workbook_Open()
Worksheets("YourSheetName").Protect Password:="secret", UserInterFaceOnly:=True End Sub
Or just take protection off before running your macro and put it on afterwards:
Sub InsertRow_Click()
ActiveSheet.Unprotect Password:="secret"
Dim i As Integer
For i = 1 To 10
ActiveSheet.ListObjects("Invoice").ListRows.Add alwaysinsert:=True
Next i
ActiveSheet.protect Password:="secret"
End Sub
Userinterfaceonly and tables looks if it's no good match
Google should provide me with ample examples but none of them seem to work
What I want: Everytime the user presses, and then releases, the ENTER key, for my program to do do something (ie. create a MsgBox, or call function Foo). I would prefer this in the form of a MWE
What I have done: I have tried googling it but none of the examples are functional. They compile, but don't do anything. I have also made sure to save in a macro compatible Excel format.
What I am using: I am using Excel 2016, 64 bit with Office 365
EDIT: The user is entering this information into the worksheet. I want to intercept the user input and everytime they press ENTER, take the cursor/active cell down two rows, so there is an empty cell below every cell. If the user presses tab, I want to take the cursor/active cell right two columns, so there si an empty cell to the right of every cell.
EDIT 2: here is a MWE of what I have right now which should work, but which does nothing. I am adding this to the worksheet, and not as a module
Sub SomeActions()
MsgBox ("Hello")
End Sub
Private Sub Workbook_Open()
Application.OnKey "~", "SomeActions"
End Sub
First, make an callback Sub that performs the logic you need. Put it into a new code module (NOT into worksheet code):
Sub SomeActions()
...
End Sub
Then, subscribe to OnKey event, for example, when the user opens the workbook (this code goes into ThisWorkbook) module in VBA editor:
Private Sub Workbook_Open()
Application.OnKey "~", "SomeActions"
End Sub
"~" means Enter key. For numeric keypad key use "{ENTER}".
What I want: Everytime the user presses, and then releases, the ENTER key, for my program to do do something (ie. create a MsgBox, or call function Foo). I would prefer this in the form of a MWE
If I read this correctly, you want a blank row between every user inputted value doing down and a blank column between every user inputted value going right.
On the worksheet code sheet:
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Count > 1 Then Exit Sub
Application.EnableEvents = False
Dim r As Long, c As Long
If Target.Row / 2 <> Int(Target.Row / 2) Then r = 1
If Target.Column / 2 <> Int(Target.Column / 2) Then c = 1
Target.Offset(r, c).Activate
Application.EnableEvents = True
End Sub
This occurs for all navigation. If you only want this to occur on input, modify to suit a Worksheet_Change event macro instead of a Worksheet_SelectionChange event macro.
Google should provide me with ample examples but none of them seem to work
What I want: Everytime the user presses, and then releases, the ENTER key, for my program to do do something (ie. create a MsgBox, or call function Foo). I would prefer this in the form of a MWE
What I have done: I have tried googling it but none of the examples are functional. They compile, but don't do anything. I have also made sure to save in a macro compatible Excel format.
What I am using: I am using Excel 2016, 64 bit with Office 365
EDIT: The user is entering this information into the worksheet. I want to intercept the user input and everytime they press ENTER, take the cursor/active cell down two rows, so there is an empty cell below every cell. If the user presses tab, I want to take the cursor/active cell right two columns, so there si an empty cell to the right of every cell.
EDIT 2: here is a MWE of what I have right now which should work, but which does nothing. I am adding this to the worksheet, and not as a module
Sub SomeActions()
MsgBox ("Hello")
End Sub
Private Sub Workbook_Open()
Application.OnKey "~", "SomeActions"
End Sub
First, make an callback Sub that performs the logic you need. Put it into a new code module (NOT into worksheet code):
Sub SomeActions()
...
End Sub
Then, subscribe to OnKey event, for example, when the user opens the workbook (this code goes into ThisWorkbook) module in VBA editor:
Private Sub Workbook_Open()
Application.OnKey "~", "SomeActions"
End Sub
"~" means Enter key. For numeric keypad key use "{ENTER}".
What I want: Everytime the user presses, and then releases, the ENTER key, for my program to do do something (ie. create a MsgBox, or call function Foo). I would prefer this in the form of a MWE
If I read this correctly, you want a blank row between every user inputted value doing down and a blank column between every user inputted value going right.
On the worksheet code sheet:
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Count > 1 Then Exit Sub
Application.EnableEvents = False
Dim r As Long, c As Long
If Target.Row / 2 <> Int(Target.Row / 2) Then r = 1
If Target.Column / 2 <> Int(Target.Column / 2) Then c = 1
Target.Offset(r, c).Activate
Application.EnableEvents = True
End Sub
This occurs for all navigation. If you only want this to occur on input, modify to suit a Worksheet_Change event macro instead of a Worksheet_SelectionChange event macro.
I have 50 datasheets in the project, and nobody remembers to run the save macro when going to another sheet. The bright idea is to use a private sub Worksheet_Deactivate to do the necessary calculations when they select another worksheet. In addition to the 50 datasheets, there are two more worksheets in the workbook for which the calculation must not run. It would be nice if the sub could be put in "Worksheets" rather than replicated 50 times in individual worksheets, but the two other worksheets need to be excluded from processing.
Problem is, the sub defaults to the deactivating worksheet (such as an unqualified "Range.Value =" in the macro code), but the active worksheet is now the worksheet being navigated TO. So any ActiveXXXXX statement directs to the wrong worksheet. Worksheet.Name is disallowed.
Datasheets are numbered 1 to 50. What is needed is a statement early in the deactivate sub similar to
If DeactivatingWorksheet(X) = "BasicInfo" Or "Constants" Then GoTo EndSub
where X is the value of the deactivating worksheet. Of course, X is known only to Excel at the moment of processing.
I can't seem to figure out how to refer to the deactivating worksheet in the macro's IF statement. Any ideas?
Use Workbook_SheetDeactivate(ByVal sh as Object) instead of Worksheet_Deactivate(). The Workbook-level event supplies the name of the sheet being departed, even though in both cases the ActiveSheet has already changed when when event fires. Use sh just like a worksheet variable - sh.Name, sh.ProtectionMode, etc.
Now you don't need 50 subs; just one. Another thing that this allows is, you can "abort" the change to the now ActiveSheet by sh.Activate to the old one (but turn off events or you'll have a lovely infinite loop).
Me also gives the old sheetname and works for the worksheet event, if you still want to go that way. Me is the old one, ActiveSheet is the new one.
If you are using Worksheet_Deactivate and this calls a subroutine in a seperate module, you can pass the name of the deactivating worksheet to the subroutine.
For instance, if your subroutine is something like:
Sub test()
ActiveSheet.Range("whatever") = "something"
ThisWorkbook.Save
End Sub
And you call it from the worksheet like:
Private Sub Worksheet_Deactivate()
Module1.test()
End Sub
You can add a parameter to the subroutine to take the worksheet name, and add a test:
Sub test(worksheetname as string)
If worksheetname <> "dontsavethistab" then
ActiveSheet.Range("whatever") = "something"
'or... you could also do:
Sheets(worksheetName).Range("Whatever") = "something"
ThisWorkbook.Save
End If
End Sub
And call it from your Worksheet_Deactivate event like:
Private Sub Worksheet_Deactivate()
Module1.test (Me.Name)
End Sub
If you wanted to get a little cleaner, instead of the worksheet name you could pass the worksheet object:
Private Sub Worksheet_Deactivate()
Module1.test(Me)
End Sub
Sub test(ws as worksheet)
If ws.name <> "dontsavethistab" then
ws.Range("Whatever") = "something"
ThisWorkbook.Save
End If
End Sub
This way you have the entire worksheet object to do with as you please in your subroutine.