I need help in locking specific range of cells based on the selection of value from list from another cell.
To be specific, I have created data validation list for columns N5 to N36, upon selection of value "Exist" from the cell N5, I want to lock that specific row O5 to U5.
i.e "Exist" in N6 would lock O6 to U6 and so on.
Similarly for the other rows till N36.
And if the user selects "Does not Exist", then I would want those cells to remain unlocked and editable similar to the above condition.
I have tried macros from various forums using my very basic knowledge of using macros, but most of those lock the entire sheet.
Code I tried:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("N5:N36")) Is Nothing Then
ActiveSheet.Unprotect
If Target.Value = "Exist" Then
Range("O" & Target.Column & ":U" & Target.Column).Select Selection.Locked = False
Else
Range("O" & Target.Column & ":U" & Target.Column).Select Selection.Locked = True
End If
End If
ActiveSheet.Protect DrawingObjects:=True, Contents:=True, Scenarios:=True
End Sub
I would really appreciate your quick help.
Thanks in Advance.
Is this what you are trying (Tried And Tested)? Also see THIS. It's worth a read.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rw As Long
Dim sPass As String
'~~> Password
sPass = "BlahBLah"
On Error GoTo Whoa
'~~> For excel 2003 use .Count instead of .CountLarge
'~~> In case of multiple cells were changed
If Target.Cells.CountLarge > 1 Then Exit Sub
Application.EnableEvents = False
If Not Intersect(Target, Range("N5:N36")) Is Nothing Then
If UCase(Trim(Target.Value)) = "EXIST" Then
rw = Target.Row
With ActiveSheet
.Unprotect sPass
.Cells.Locked = False
.Range("O" & rw & ":U" & rw).Locked = True
.Protect Password:= sPass , DrawingObjects:=True, _
Contents:=True, Scenarios:=True
End With
End If
End If
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
you could do something like this:
Sub LockCells()
'unprotect the sheet
ActiveSheet.Unprotect
'unlock all cells
Cells.Locked = False
Cells.FormulaHidden = False
Dim cell As Range
'find all cells that need to be locked
For Each cell In Range("N5:N36")
If cell = "Exist" Then
Range("O" & cell.Row & ":U" & cell.Row).Locked = True
Range("O" & cell.Row & ":U" & cell.Row).FormulaHidden = True
End If
Next cell
'protect the sheet
ActiveSheet.Protect DrawingObjects:=True, Contents:=True, Scenarios:=True
End Sub
Related
I am having problems with a "Worksheet_Change" sub that copies and pastes the whole row into a second worksheet ("Completed") when the column "P" takes on the value "x". It reads like this:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
Application.EnableEvents = False
'If Cell that is edited is in column P and the value is x then
If Target.Column = 16 And Target.Value = "x" Then
'Define last row on completed worksheet to know where to place the row of data
LrowCompleted = Sheets("Completed").Cells(Rows.Count, "A").End(xlUp).Row
'Copy and paste data
Range("A" & Target.Row & ":P" & Target.Row).Copy Sheets("Completed").Range("A" & LrowCompleted + 1)
'Delete Row from Project List
Range("A" & Target.Row & ":P" & Target.Row).Delete xlShiftUp
End If
Application.EnableEvents = True
End Sub
The sub itself works fine but if I copy and paste anywhere in the worksheet, the sub is activated and the row into which I paste is send to my "Completed" sheet.
I have played around with the "if-clause" without any luck so far. E.g.:
If Not Target.Column = 16 And Target.Value = "x" Is Nothing Then
I fear I am missing the obvious and I am grateful for any help.
Thanks and regards
PMHD
If you are concerned with muliple targets, deal with them; don't discard them.
Private Sub Worksheet_Change(ByVal Target As Range)
If not intersect(target, range("p:p")) is nothing then
on error goto meh
Application.EnableEvents = False
dim t as range, lrc as long
lrc = workSheets("Completed").Cells(Rows.Count, "A").End(xlUp).Row + 1
for each t in intersect(target, range("p:p"))
if lcase(t.Value2) = "x" Then
intersect(columns("A:P"), t.rows(t.row)).Copy _
destination:=workSheets("Completed").cells(lrc , "A")
lrc = lrc+1
'Delete Row from Project List
intersect(columns("A:P"), t.rows(t.row)).Delete xlShiftUp
end if
next t
End if
meh:
Application.EnableEvents = true
end sub
Thanks, Jeeped.
The problem arose due to Target referring to multiple cells. It was fixed by excluding cases where Target.Count > 1.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
Application.EnableEvents = False
'Exclude all cases where more than one cell is Target
If Target.Count > 1 Then
'If Cell that is edited is in column P and the value is x then
ElseIf Target.Column = 16 And Target.Value = "x" Then
'Define last row on completed worksheet to know where to place the row of data
LrowCompleted = Sheets("Completed").Cells(Rows.Count, "A").End(xlUp).Row
'Copy and paste data
Range("A" & Target.Row & ":P" & Target.Row).Copy Sheets("Completed").Range("A" & LrowCompleted + 1)
'Delete Row from Project List
Range("A" & Target.Row & ":P" & Target.Row).Delete xlShiftUp
End If
Application.EnableEvents = True
End Sub
I'm using Private Sub Worksheet_Change(ByVal Target As Range) to react to a changes in Range("AV9:AV" & lastrow) in each of this cells is a dropdown list which is defined as follow:
Dim lastrow2 As Long
Dim lastcell As Long
lastrow2 = Tabelle3.Range("A" & Rows.Count).End(xlUp).Offset(8).Row
lastcell = Tabelle3.Range("AH1048576").End(xlUp).Row
For Each Cell In Tabelle3.Range(Tabelle3.Cells(9, 48), Tabelle3.Cells(lastcell, 48))
If Cell = "" Then
Dim MyList(2) As String
MyList(0) = "Relevant"
MyList(1) = "For Discussion"
MyList(2) = "Not Relevant"
With Tabelle3.Range("AV9:AV" & lastrow2).Validation
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, Formula1:=Join(MyList, Application.International(xlListSeparator))
End With
End If
Next
Those lines are incorporated into a macro which fills Tabelle3with data and all necessary functions, such as the dropdown field.
The Private Sub Worksheet_Change(ByVal Target As Range) is defined as follow:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lastrow As Long
lastrow = Tabelle3.Range("A" & Rows.Count).End(xlUp).Offset(8).Row
On Error Resume Next
If Not Intersect(Target, Range("AV9:AV" & lastrow)) Is Nothing And Target.Value = "Relevant" Or Target.Value = "For Discussion" Then
Application.CutCopyMode = False
Cells(Target.Row, "A").Resize(, 57).Copy
Tabelle14.Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Tabelle14.Range("A" & Rows.Count).End(xlUp).PasteSpecial xlPasteFormats
Tabelle14.Range("A" & Rows.Count).End(xlUp).PasteSpecial xlPasteColumnWidths
Application.CutCopyMode = False
End If
If Not Intersect(Target, Range("AV9:AV" & lastrow)) Is Nothing And Target.Value <> "" Then
Cells(Target.Row, "A").Resize(, 2).Copy
Tabelle10.Range("A" & Rows.Count).End(xlUp).Offset(1).PasteSpecial xlPasteValues
Application.CutCopyMode = False
End If
'//Delete all duplicate rows
Set Rng = Tabelle10.UsedRange
Rng.RemoveDuplicates Columns:=Array(1)
End Sub
As you can see the first part of the Private Sub Worksheet_Change(ByVal Target As Range) 'should' only be executed If in a dropdown field in Range("AV9:AV" & lastrow) the option 'Relevant' or 'For Discussion' is selected and the second part If anything is selceted , therefore I have used Target.Value <> "". This is principally working fine but one bug occurs.
If I insert the data to Tabelle3 through the already mentioned macro, it seems the Private Sub Worksheet_Change(ByVal Target As Range) is then automatically executed for row 9 in Tabelle3and I can find its data in Tabelle14 and Tabelle10 as defined.
Does someone know what's going on here?
Try making these changes:
Option Explicit
Public Sub SetTabelle3Validation()
Const V_LIST = "Relevant,For Discussion,Not Relevant"
Dim ws As Worksheet: Set ws = Tabelle3
Dim lr As Long: lr = ws.Range("AV" & ws.Rows.Count).End(xlUp).Row
Dim app As Application: Set app = Application
Dim fc As Range
If lr > 9 Then
Set fc = ws.Range(ws.Cells(9, "AV"), ws.Cells(lr, "AV"))
fc.Validation.Delete
fc.AutoFilter Field:=1, Criteria1:="<>"
If fc.SpecialCells(xlCellTypeVisible).Cells.Count > 1 Then
app.EnableEvents = False
app.ScreenUpdating = False
With fc.SpecialCells(xlCellTypeVisible).Validation
.Add Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Operator:=xlBetween, _
Formula1:=Join(Split(V_LIST, ","), app.International(xlListSeparator))
End With
app.ScreenUpdating = True
app.EnableEvents = True
End If
fc.AutoFilter
End If
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim lr As Long: lr = Me.Rows.Count
Dim lrT3 As Long: lrT3 = Me.Range("A" & lr).End(xlUp).Offset(8).Row
Dim app As Application: Set app = Application
Dim inAV As Boolean
inAV = Not Intersect(Target, Me.Range("AV9:AV" & lrT3)) Is Nothing
With Target
If .Cells.CountLarge > 1 Or Not inAV Or Len(.Value) = 0 Then Exit Sub
app.EnableEvents = False
If .Value = "Relevant" Or .Value = "For Discussion" Then
Me.Cells(.Row, "A").Resize(, 57).Copy
With Tabelle14.Range("A" & lr).End(xlUp).Offset(1)
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
.PasteSpecial xlPasteColumnWidths
End With
Tabelle14.UsedRange.RemoveDuplicates Columns:=Array(1)
End If
Me.Cells(.Row, "A").Resize(, 2).Copy
With Tabelle10
.Range("A" & lr).End(xlUp).Offset(1).PasteSpecial xlPasteValues
.UsedRange.RemoveDuplicates Columns:=Array(1)
End With
app.CutCopyMode = False
app.EnableEvents = True
End With
End Sub
In SetTabelle3Validation()
Replace For loop with AutoFilter for speed
Turn Application.EnableEvents Off to stop triggering Worksheet_Change() (then back On)
In Worksheet_Change()
Exit the Sub if pasting multiples values, Target is not in col AV, or is empty
Else (Target is in col AV, and not empty)
Turn Application.EnableEvents Off
If Target value is "Relevant" Or "For Discussion", update Tabelle14
Else (Target value is "Not Relevant"), update Tabelle10
Turn Application.EnableEvents On
Assumptions
All objects starting with Tabelle are the Code Names of other sheets
Worksheet_Change() belongs to Tabelle3
I have found a similar article about my question, as stated below;
How do I track who uses my Excel spreadsheet?
However, I do like the last coloumn of comment >>
"You could also put a time stamp in the next column to show when the spreadsheet was used"
My question is> can anyone guide me the possible step or let me copy the code for doing this please? and how to hide the worksheet without anyone noticing?
My key is, very importantly, everything must done silently which no one else (other users in sharedrive) could find out i m tracking it. The reason is , i have done lot of research worksheets, and i don't have time/impossible to make every single excel worksheet perfect, i need to prioritize them inorder to be efficient with my time by knowing which one is more important to people.
many thanks~!!
In Excel, under the Review tab, you have 'Track Changes'. This should do everything you want.
If you want a VBA script to do this, try one of the following code samples.
Private Sub Worksheet_Change(ByVal Target As Range)
Set t = Target
Set a = Range("A:A")
If Intersect(t, a) Is Nothing Then Exit Sub
Application.EnableEvents = False
t.Offset(0, 7).Value = Environ("username")
Application.EnableEvents = True
End Sub
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
Dim V As Long
Application.EnableEvents = False
Set rng1 = Application.Union(Range("a1:g1"), Range("H:iv"))
Set rng = Application.Intersect(Target, rng1)
If Not rng Is Nothing Then Exit Sub
V = Target.Offset(0, 12).Value
If Target.Offset(0, 12) = "" Then
With Range("H" & Target.Row)
.Value = Target.Address & ": first entry by " & Application.UserName & " at " & Now()
.ColumnWidth = 60
.Interior.ColorIndex = 33
End With
Target.Offset(0, 12).Value = Target.Value
Application.EnableEvents = True
Exit Sub
End If
Target.Offset(0, 12).Value = Target.Value
With Range("H" & Target.Row)
.Value = Target.Address & " changed from " & V & " to " & Target.Value & " by " & Application.UserName & " at " & Now()
.ColumnWidth = 60
.Interior.Color = vbYellow
End With
Application.EnableEvents = True
End Sub
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
With Target
If .Count > 1 Then Exit Sub
If Not Intersect(Range("A2:A10"), .Cells) Is Nothing Then
Application.EnableEvents = False
Sheets("Sheet2").Select
If IsEmpty(.Value) Then
.Offset(0, 1).ClearContents
Else
With .Offset(0, 1)
.NumberFormat = "dd mmm yyyy hh:mm:ss"
.Value = Now
End With
End If
Sheets("Sheet1").Select
Application.EnableEvents = True
End If
End With
End Sub
All of these 'Worksheet_Change' scripts are worksheet events. You need to right-click your sheet and click 'View Code' then paste the script into the window that opens. Try one at a time, not all three together.
I'd like to know how to detect if the user is deleting or inserting content into a range. If they are deleting a range say D14:D18. I'd like to then perform a macro that also deletes content in E14:E18. I just wouldn't want to delete E14:E18 if they are entering content into D14:D18.
I've tried:
If Selection.ClearContents Then
MsgBox Target.Offset(0, 3).Style
End If
But this get's me stuck in an infinite loop.
A bit more context:
I have a few hundred cells in D:D for entering quantities for services. Not everything in D:D should be touched. Only cells in D:D with .Style = "UnitInput". In E:E I have data validation that lets the user only enter contractor 1 or contractor 2 But, when content is entered in D:D I run a macro to assign the default contractor (housed in F:F) to E:E. So when the user enters quantities into D:D it correctly assigns the default contractor. And when they delete singular items from D:D I have it handling proper removal of contractors. It's only when they delete a range of items from D:D.
Full code:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.ScreenUpdating = False
On Error GoTo ErrHandler:
If Selection.Rows.Count * Selection.Columns.Count = 1 Then
If Target.Offset(0, 3).Style = "Contractor" Then
If Target.Value < 1 Then
Target.Offset(0, 3).Value = ""
Else
Target.Offset(0, 3).Value = Target.Offset(0, 2).Value
End If
End If
If Target.Offset(0, 5).Style = "Markup" Then
If Target.Value = "" Then
Target.Offset(0, 5).Value = ""
ElseIf Target.Value <= Target.Offset(0, 14).Value Then
Target.Offset(0, 5).Value = "Redact 1"
ElseIf Target.Value >= Target.Offset(0, 15).Value Then
Target.Offset(0, 5).Value = "Redact 2"
Else
Target.Offset(0, 5).Value = "Redact 3"
End If
End If
Else
'!!!!!! this is where I need to handle multiple deletions. !!!!!!!
End If
Application.ScreenUpdating = True
ErrHandler:
Application.ScreenUpdating = True
Resume Next
End Sub
Based on your comments in chat, here is what I propose
UNTESTED
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, aCell As Range
Dim lRow As Long
'~~> Error handling, Switching off events and Intersect
'~~> As described in
'~~> http://stackoverflow.com/questions/13860894/ms-excel-crashes-when-vba-code-runs
On Error GoTo Whoa
Application.EnableEvents = False
With ActiveSheet
'~~> Find Last Row since data is dynamic
'~~> For further reading see
' http://stackoverflow.com/questions/11169445/error-in-finding-last-used-cell-in-vba
If Application.WorksheetFunction.CountA(.Cells) <> 0 Then
lRow = .Cells.Find(What:="*", _
After:=.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
End If
If lRow > 12 Then
'~~> Set your range
Set rng = Range("D13:D" & lRow)
If Not Intersect(Target, rng) Is Nothing Then
For Each aCell In rng
If Len(Trim(aCell.Value)) = 0 Then
Select Case Target.Offset(0, 3).Style
Case "Contractor"
'~~> Do Your Stuff
Case "Markup"
'~~> Do Your Stuff
'
'~~> And so on
'
End Select
End If
Next aCell
End If
End If
End With
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
Here is an idea -- you have to first select a region to clear its contents. Use selection change to record the number of non-blank cells and then worksheet change to see if it drops to zero. Something like:
Dim NumVals As Long
Private Sub Worksheet_Change(ByVal Target As Range)
Dim NewCount As Long
NewCount = Application.WorksheetFunction.CountA(Target)
If NewCount = 0 And NumVals > 0 Then MsgBox Target.Address & " was cleared"
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
NumVals = Application.WorksheetFunction.CountA(Target)
End Sub
I have this code is Sheet1 and it seems to capture when I highlight a group of cells (which contains at least one value) and then hit the delete key.
You can use the CommandBars Undo Control to determine if the user has actually deleted something.
Bear in mind this will fire if the user any or all of the contents of the Range D14:D18, but can be adjusted in many ways to suit your exact needs. After seeing your edit, this basically means you can adjust the ranges and need be and which cells in column E it affects as well. If you need more guidance on this, let me know.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("D14:D18")) Is Nothing Then
Dim sLastAction As String
sLastAction = Application.CommandBars("Standard").Controls("&Undo").List(1)
Debug.Print sLastAction
'manual delete 'right-click delete 'backspace delete
If sLastAction = "Clear" Or sLastAction = "Delete" Or Left(sLastAction, 9) = "Typing ''" Then
Application.EnableEvents = False
Me.Range("E14:E18").ClearContents
Application.EnableEvents = True
End If
End If
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ChangedRange As Range
Dim Area As Range
Dim Cell As Range
Set ChangedRange = Application.Intersect(Target, Range("D:D"))
If Not ChangedRange Is Nothing Then
Application.EnableEvents = False
For Each Area In ChangedRange.Areas
For Each Cell In Area
If IsEmpty(Cell) Then
Cell.Offset(0, 1).ClearContents
End If
Next
Next
Application.EnableEvents = True
End If
End Sub
I want to make a code that adds a certain value to the first column if values are added to a certain range. And delete that value if that range is empty.
This is what I have so far, but I keep getting errors and I can't seem to figure out what I'm doing wrong.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, cell As Range
On Error GoTo haveError
Set rng = Application.Intersect(Target, Me.Range("B1:G100"))
If Not rng Is Nothing Then
Application.EnableEvents = False
For Each cell In rng.Cells
If cell.Value = "blah" Then
Range("A" & cell.Row).Value = "derp"
End If
Next
For Each cell In rng.Cells
If Range("B" & cell.Row, "G" & cell.Row).Value = "" Then
Range("A" & cell.Row).ClearContents
End If
Next
Application.EnableEvents = True
End If
Exit Sub
haveError:
MsgBox Err.Description
Application.EnableEvents = True
End Sub
You got Type mismatch error for wrong syntax in this line:
If Range("B" & cell.Row, "G" & cell.Row).Value = "" Then
So, I modified it and it work well. I also reduce one looping because both condition can set in only one looping.
Here, the full code:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, cell As Range
On Error GoTo haveError
Set rng = Application.Intersect(Target, Me.Range("B1:G100"))
If Not rng Is Nothing Then
Application.EnableEvents = False
For Each cell In rng.Cells
If cell.Value = "blah" Then
Me.Range("A" & cell.Row).Value = "derp"
End If
If WorksheetFunction.CountA(Me.Range("B" & cell.Row & ":" & "G" & cell.Row)) = 0 Then
Me.Range("A" & cell.Row).ClearContents
End If
Next
Application.EnableEvents = True
End If
Exit Sub
haveError:
MsgBox Err.Description
Application.EnableEvents = True
End Sub
I've reorganized some commands and only performed actions when they are necessary.
Private Sub Worksheet_Change(ByVal Target As Range)
'don't do anything unless there is something to do
If Not Intersect(Target, Me.Range("B1:G100")) Is Nothing Then
On Error GoTo haveError
'don't declare vars until you kow you will need them
Dim rng As Range, cell As Range
Application.EnableEvents = False
Set rng = Application.Intersect(Target, Me.Range("B1:G100"))
For Each cell In rng.Cells
If cell.Value = "blah" Then
Range("A" & cell.Row).Value = "derp"
ElseIf Application.CountBlank(Cells(cell.Row, "B").Resize(1, 6)) = 6 Then
Cells(cell.Row, "A").ClearContents
End If
End If
GoTo safeExit
haveError:
If CBool(Err.Number) Then
'Debug.Print Err.Number & ": " & Err.Description
MsgBox Err.Number & ": " & Err.Description
Err.Clear
End If
safeExit:
Set rng = Nothing
Application.EnableEvents = True
End Sub
Rather than having two For Each...Next Statement, I've used an If ... ElseIf ... End If since the conditions are mutually exclusive (i.e. if one is true, the other cannot be true).