I'm trying to loop through a range of cells, locking any cell that has content while leaving empty cells unlocked.
When I run the below code the result is the entire sheet is locked. If I add an else statement the sheet is unlocked. Basically whatever the last .locked = (true, false) statement is is how the entire sheet winds up.
Change 1 Is it possible that I have some setting on/off that is interfering since I'm the only one who is unable to get any of this to work?
Sub ProtectTheSheet()
Dim chCell As Range
Dim chRng As Range
'Clear the default status
ActiveSheet.Unprotect
Range("A7:I35").Locked = False
Set chRng = ActiveSheet.Range("A7:I35")
'Check cell value in body and lock cells with content
For Each chCell In chRng.Cells
If chCell.Value <> "" Then Cells.Locked = True
Next chCell
ActiveSheet.Protect
End Sub
Sub ProtectTheSheet()
Dim chCell As Range
Dim chRng As Range
ActiveSheet.Unprotect
Set chRng = ActiveSheet.Range("A7:I35")
'Check cell value in body and lock cells with content
For Each chCell In chRng.Cells
chCell.Locked = (chCell.Value <> "")
Next chCell
ActiveSheet.Protect
End Sub
You can try this.
Public Sub abc()
ActiveSheet.Unprotect Password:="1234"
ActiveSheet.Range("I8:I500, K8:K500, M8:M500, N8:N500").Cells.Locked = False
ActiveSheet.Protect Password:="1234"
End Sub
Check this out: http://www.mrexcel.com/archive/VBA/15950b.html
Sub CellLocker()
Cells.Select
' unlock all the cells
Selection.Locked = false
' next, select the cells (or range) that you want to make read only,
' here I used simply A1
Range("A1").Select
' lock those cells
Selection.Locked = true
' now we need to protect the sheet to restrict access to the cells.
' I protected only the contents you can add whatever you want
ActiveSheet.Protect DrawingObjects:=false, Contents:=true, Scenarios:=false
End Sub
If you say Range("A1").Select, then it locks only A1. You can specify multiple cells to be locked by specifying as follows:
A3:A12,D3:E12,J1:R13,W18
This locks A3 to A12 and D3 to E12 etc.
I may be missing something but...
Cells.Locked = True
...will lock all cells on the active sheet. If you just change it to...
chCell.Locked = True
...then it works; I think?! As the range is very small, you may as well not unlock cells at the start, and instead unlock cells whilst locking them e.g.
For Each chCell In chRng.Cells
If chCell.Value <> "" Then
chCell.Locked = True
Else
chCell.Locked = False
End If
Next chCell
If you are new to VBA, I would recommend cycling through code line-by-line as described in this Excel consultant's video. If you step through code, you can check "has cell A7 behaved as expected?"...instead of just seeing the end product
A quick way to unlock non-blank cells is to use SpecialCells see below.
On my testing this code handles merged cells ok, I think this is what is generating your error on Tim's code when it looks to handle each cell individually (which to be clear is not an issue in Tim's code, it is dealing with an unexpected outcome)
You may also find this article of mine A fast method for determining the unlocked cell range useful
Sub Quicktest()
Dim rng1 As Range
Dim rng2 As Range
On Error Resume Next
Set rng1 = ActiveSheet.Range("A7:I35").Cells.SpecialCells(xlFormulas)
Set rng2 = ActiveSheet.Range("A7:I35").Cells.SpecialCells(xlConstants)
On Error GoTo 0
ActiveSheet.Unprotect
ActiveSheet.Range("A7:I35").Cells.Locked = False
If Not rng1 Is Nothing Then rng1.Cells.Locked = True
If Not rng2 Is Nothing Then rng2.Cells.Locked = True
ActiveSheet.Protect
End Sub
I know this is an old thread, but I've been stuck on this for a while too, and after some testing on Excel 2013 here's what I conclude if your range includes any merged cell
The merged cells must be entirely included within that range (e.g. the merging must be entirely within the range being lock/unlocked
The range being merged can be larger, or at least exactly the range corresponding to the merged cells. If it's a named range that works as well.
Also, you cannot lock/unlock a cell that is already within a protected range. E.g if you run:
public sub test()
Sheet1.range("myNameRange").locked = true
Sheet1.protect
end sub
Twice it will work the first time, and fail the second time around. So you should unprotect the target range (or the sheet) before....
If you want to protect the specific cells of any specific excel without the password protection then here is the solution:
Sub ProtectingSheet()
Workbooks.Open (c\documents\....)
Dim mainworkBook As Workbook
Set mainworkBook = ActiveWorkbook
Worksheets(CellValue).Activate
mainworkBook.Sheets("Sheet1").Range("A1:AA100").Locked = True
Range(Cells(1, 2), Cells(1, 25)).Select
Selection.Locked = False
ActiveSheet.Protect
End Sub
Related
I'm copying values as part of one sub process and pasting value through an update button on userform.
To copy values:
Private Sub Month1_Click()
Application.DisplayAlerts = False
Application.ScreenUpdating = False
Application.AskToUpdateLinks = False
Dim wkb As Workbook
Dim wks As Worksheet
Set wkb = Workbooks.Open("Place on drive")
Set wks = wkb.Sheets("Training1")
wks.Range("Start:Finish").Copy
wkb.Close
Application.DisplayAlerts = True
Application.AskToUpdateLinks = True
Application.ScreenUpdating = True
End Sub
To paste values in current sheet:
Private Sub UpdateActuals_Click()
For i = 1 To 12
If Me.Controls("Month" & i).Value = True Then
ThisWorkbook.Sheets("2017 Actuals").Range(i+1, 5).PasteSpecial xlPasteValues
End If
Next i
End Sub
If I replace "i+1, 5" with "B5", it errors with
"PasteSpecial method of Range class failed".
I feel as if values copied in one sub process are not brought to second one, would that be correct?
Also, how do I reduce processing time given that I have 12 months (12 files) in various places that I can't change the location for...
Range usually likes a starting cell and an ending cell. I suggest since you are looking at just one cell that you change .Range to .Cells. If you really want to use a range with RC format, .Range(Cells(row1, col1), Cells(row2, col2)), if you want just one cell then you can make the two parts the same. I have run into problems before using Range and only one cell definition before, either make it .Cells for your target or fill out Range the way I have explained.. Cheers.
Dim 2017actWS AS Worksheet
Set 2017actWS = ThisWorkbook.Worksheets("2017 Actuals")
1)
2017actWS.Cells(i+1, 5).PasteSpecial xlPasteValues
-or-
2)
2017actWS.Range(2017actWS.Cells(i+1, 5), 2017actWS.Cells(i+1,5)).PasteSpecial xlPasteValues
When using Ranges excel will often throw errors if they are not the same size in a copy and paste, you can eliminate that by using a single cell as the starting target of your paste with .Cells
Also I don't see you call your function. You will want your paste close to your copy or you might find things get strange (suggestion: just after your copy).
Edited to be sure there is not worksheeet ambiguity. Thank you Scott C.
Cheers, WWC
At this point there are two problems, but the first one i want to deal with is that i cannot get the paste function to work. When I run through the code the specific cells are highlighted to copy (the cell border is b&w flashing) and the cells where they are to end up are now highlighted, but nothing pastes.
Sub OtherTask()
Dim DRng As Range
ActiveSheet.Range("g2:ah2").find(Date).Select
ActiveCell.Resize(5).Offset(5).Select
Selection.AutoFilter field:=1, Criteria1:="1", Operator:=xlFilterValues
Set DRng = ThisWorkbook.ActiveSheet.AutoFilter.Range.SpecialCells(xlCellTypeVisible)
DRng.Copy
ActiveSheet.Range("r12").PasteSpecial xlPasteAll
If ActiveSheet.AutoFilterMode = "True" Then
ActiveSheet.AutoFilterMode = "False"
End If
End Sub
I should bring up the second problem. When I execute this from the macro button it performs as per the description above, but when I am in the editor and I press the play button I get error 91 that the object is not set. Not sure why I would get the error with one form of execution and not the other?? Looking through similar perhaps I should be using value instead of copy? Thanks for any help.
I had to make some assumptions with your code because there are some things that are not that clear. The assumptions should be easy to see and to change according to your needs.
Sub OtherTask()
Dim ws as Worksheet
Dim DRng As Range
Set ws = Worksheets("mySheet")
With ws
Dim rFound as Range
Set rFound = .Range("g2:ah2").find(Date)
rFound.Resize(5).Offset(5).AutoFilter field:=1, Criteria1:="1", Operator:=xlFilterValues
'declare this range explicitly, whatever it is
Set DRng = .Range("A1:B5000").AutoFilter.Range.SpecialCells(xlCellTypeVisible)
DRng.Copy .range("R12") 'since you paste everything just do straight from copy method
If .AutoFilterMode = "True" Then .AutoFilterMode = "False"
End With
End Sub
I am trying to write a macro that will lock any cell greater than 0. When I run the code below it works but locks the 1st row where I have a drop down arrow that does sorting and number filters. Is there a way to add to this code so that the first row wont be locked?
Sub Test()
Dim Cell As Range
Dim MyPlage As Range
With ThisWorkbook.ActiveSheet
.Unprotect
.Cells.Locked = False
Set MyPlage = .Range("J2:AA1074")
For Each Cell In MyPlage
If Not IsError(Cell) Then
If Cell.Value > "0" Then
Cell.Locked = True
End If
End If
Next
.Protect
End With
End Sub
The most simplest was is to define your range which doesn't include the Top Row :)
Change
.Range("J2:AA1074")
to
.Range("J3:AA1074")
Also, Instead of looping through every cell in the range and checking if that cell has an error or not, you can directly use SpecialCells. For example (TRIED AND TESTED)
Sub Sample()
Dim Cell As Range, MyPlage As Range, FinalRange As Range
With ThisWorkbook.ActiveSheet
.Unprotect
.Cells.Locked = False
On Error Resume Next
Set MyPlage = .Range("J3:AA1074").SpecialCells(xlCellTypeConstants)
On Error GoTo 0
If Not MyPlage Is Nothing Then
For Each Cell In MyPlage
If Cell.Value > 0 Then Cell.Locked = True
Next
End If
.Protect DrawingObjects:=True, _
Contents:=True, _
Scenarios:=True, _
AllowFiltering:=True, _
AllowSorting:=True
.EnableSelection = xlUnlockedCells
End With
End Sub
To ensure that Autofilter and Sorting works, specify it in .Protect as I have done above.
Before you run the above code, you also need to take one extra step.
Unprotect the worksheet if it is already protected
Under Review Tab, click on "Allow Users to Edit Ranges"
Add "New" range
Select the range you want allow users to sort
Screenshot
You can add following code to the Sheet module (change Range("J1:AA1") to the range with your autofilter):
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Application.Intersect(Target, Range("J1:AA1")) Is Nothing Then
ActiveSheet.Protect
Else
ActiveSheet.Unprotect
End If
End Sub
I would like to write some vba code that monitors the OnChange event for a sheet and does some adjustment if text does not fit a cell.
I.e. make the text smaller or wrap etc..
I know a can have Excel to automatically shrink the text and I know how to enable wrap in vba, but...
how do I check in vba whether the text fits in a cell to begin with?
Quick and dirty way which will not require you to check each and every cell.
I use this method to usually show all the data.
Sub Sample()
With Thisworbook.Sheets("Sheet1").Cells
.ColumnWidth = 254.86 '<~~ Max Width
.RowHeight = 409.5 '<~~ Max Height
.EntireRow.AutoFit
.EntireColumn.AutoFit
End With
End Sub
I use this method if I want to wrap the text (If Applicable) and keep the row width constant
Sub Sample()
With Thisworbook.Sheets("Sheet1").Cells
.ColumnWidth = 41.71 '<~~ Keep the column width constant
.RowHeight = 409.5
.EntireRow.AutoFit
End With
End Sub
Note: This is not applicable for merged cells. For that there is a separate method.
I'm using THE "dirty" method - that's only one I know: force AutoFit and check new width/height.
However, we can't grantee that was chosen cell that forced new fit. So I opt by copying cell content to an empty worksheet.
That, of course, cause a lot of other problems, and more workarounds.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Fits(Target) Then
'Notice that Target may have multiple cells!!!
End If
End Sub
Function Fits(ByVal Range As Range) As Boolean
Dim cell As Range, tmp_cell As Range, da As Boolean, su As Boolean
'Stores current state and disables ScreenUpdating and DisplayAlerts
su = Application.ScreenUpdating: Application.ScreenUpdating = False
da = Application.DisplayAlerts: Application.DisplayAlerts = False
'Creates a new worksheet and uses first cell as temporary cell
Set tmp_cell = Range.Worksheet.Parent.Worksheets.Add.Cells(1, 1)
'Assume fits by default
Fits = True
'Enumerate all cells in Range
For Each cell In Range.Cells
'Copy cell to temporary cell
cell.Copy tmp_cell
'Copy cell value to temporary cell, if formula was used
If cell.HasFormula Then tmp_cell.Value = cell.Value
'Checking depends on WrapText
If cell.WrapText Then
'Ensure temporary cell column is equal to original
tmp_cell.ColumnWidth = cell.ColumnWidth
tmp_cell.EntireRow.AutoFit 'Force fitting
If tmp_cell.RowHeight > cell.RowHeight Then 'Cell doesn't fit!
Fits = False
Exit For 'Exit For loop (at least one cell doesn't fit)
End If
Else
tmp_cell.EntireColumn.AutoFit 'Force fitting
If tmp_cell.ColumnWidth > cell.ColumnWidth Then 'Cell doesn't fit!
Fits = False
Exit For 'Exit For loop (at least one cell doesn't fit)
End If
End If
Next
tmp_cell.Worksheet.Delete 'Delete temporary Worksheet
'Restore ScreenUpdating and DisplayAlerts state
Application.DisplayAlerts = da
Application.ScreenUpdating = su
End Function
Has solution got too complex, there may be some problems I didn't preview.
This won't work in read-only workbooks, however, cells in read-only workbooks don't change as well!
I am generating a Daily Planner Sheet in which i want to lock some appraisal cells after saving. I have written the following code in excel workbook code. The macro asks to enter password before saving. Why is it asking to enter the password?(I have 53 sheets for weekly planning. I have shown only 2 here)
Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
Sheets("Sheet18").Unprotect Password:="****"
Sheets("Sheet19").Unprotect Password:="****"
ActiveSheet.Protect Contents:=False
For Each Cell In Range("H5:H24,J5:J24")
If Cell <> "" Then Cell.Locked = True
If Cell = "" Then Cell.Locked = False
Next
ActiveSheet.Protect Contents:=True
Sheets("Sheet18").Protect Password:="****"
Sheets("Sheet18").Protect UserInterfaceOnly:=True
Sheets("Sheet19").Protect Password:="****"
Sheets("Sheet19").Protect UserInterfaceOnly:=True
End Sub
Your code will behave differently depending on the active sheet when saving.
Additionnaly, I wouldn't recommend to overload the reserved name "Cell" with a local loop variable. This will lead to unexpected behavior.
You should remove references to ActiveSheet.
If your wish is to protect the entire workbook, I would suggest iteration over the worksheets:
Sub ProtectAll()
Dim wSheet As Worksheet
Dim myCell As Range
For Each wSheet In Worksheets
wSheet.Unprotect Password:="****"
For Each myCell In Range("H5:H24,J5:J24")
myCell.Locked = (myCell <> "")
Next myCell
wSheet.Protect Contents:=True, Password:="****", UserInterfaceOnly:=True
Next wSheet
End Sub
NB: you have to put the code in a code module.