I'm creating a quiz.
Questions on one worksheet and the answers on another.
When a question is answered another field with this formula
=IF(C5="","",IF(C5=Answers!A5,"Correct","Incorrect"))
tells the person if the answer is correct or incorrect.
I am using data validation with dropdown lists so they can only choose true/false, (a, b, c, d) etc.
Is there a way to lock in a selected answer, until a master reset button is pressed?
For example,
Question is in A1
The possible answers are in the form of a dropdown menu in B1.
Sometimes the answer is in the form of a true false, sometime it is in the form of a multiple choice. In the example of true false, if the person puts in true, c3 will say correct or if they put false, then incorrect.
As it is now, the person can switch back and forth as much as they want.
I am looking to make it so once an answer is selected, they cannot change it.
You can use Sheet Protection, combined with Range Locking and a Change event.
Put this code in the relevant Worksheet Module. Adjust the Private constants to suit your needs.
Option Explicit
' Reference the cells that your users may enter data into
Private Const DataCells As String = "J1,J3,J5"
Private Const PW As String = "password"
Private Sub Worksheet_Change(ByVal Target As Range)
Dim cl As Range
Dim DataRange As Range
Set DataRange = Me.Range(DataCells)
'Loop thru changed cells
For Each cl In Target.Cells
'If changed cell is in the DataCells range and is not blank, lock it
If Not Application.Intersect(cl, DataRange) Is Nothing Then
If Not IsEmpty(cl) Then
Me.Unprotect PW
Target.Locked = True
Me.Protect PW
End If
End If
Next
End Sub
'Re-enable data entry to all DataCells
Sub MasterReset()
'Unlock the sheet, prompt for password
Me.Unprotect
'Unlock the cells
Me.Range(DataCells).Locked = False
'Optional, clear DataCells
Me.Range(DataCells).ClearContents
'Lock the sheet again
Me.Protect PW
End Sub
This works pretty good:
In the "This Workbook" module, insert the code:
Private Sub Workbook_Open()
Sheet1.Protect userinterfaceonly:=True 'allows macros to run
Sheet1.Range("A1:A20").Locked = False 'replace this range with the range the user deals with.
End Sub
In the sheet module that the user will be interacting with, add this code:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Locked = True Then Exit Sub
If Target.Locked = False Then
If Target.Value = "" Then Exit Sub
If Target.Value <> "" Then Target.Locked = True
End If
End Sub
That should take care of things for you!
Related
I have 4 Cells (S11:T12) and a Command Button 1, what I want is, until all 4 cells are populated the command button should be disabled (Which I can do from the properties tab) and once all 4 cells are filled with number, the command button should be enabled and once the data from these cells are deleted, the command button should be disabled again.
Under which event should I write the code?
I tried this, but it does not work.
Private Sub Workbook_Open(Cancel As Boolean)
If Sheets("WorkArea").Range("S11:T12") = "" Then
Sheets("WorkArea").CommandButton1.Enabled = False
Else
Sheets("WorkArea").CommandButton1.Enabled = True
End If
End Sub
Use the WorkSheet_Change event handler to handle the change in cells, and you can use the CountBlank worksheet function to determine if a range is empty.
Private Sub Worksheet_Change(ByVal Target As Range)
If WorksheetFunction.CountBlank(Range("S11:T12")) = 4 Then
Sheets("WorkArea").CommandButton1.Enabled = False
Else
Sheets("WorkArea").CommandButton1.Enabled = True
End If
End Sub
Worksheet_Change
CountBlank
According to your question however, you actually want:
Private Sub Worksheet_Change(ByVal Target As Range)
If WorksheetFunction.CountBlank(Range("S11:T12")) = 0 Then
Sheets("WorkArea").CommandButton1.Enabled = True
Else
Sheets("WorkArea").CommandButton1.Enabled = False
End If
End Sub
A Worksheet Change
This solution will not work if the critical range contains formulas.
To count the number of cells that are not empty you can use the WorksheetFunction.CountA method.
Usually you don't want this code to run when there are changes outside of the range, so you will restrict the code to the range with the Application.Intersect method. You don't have to enable or disable the command button on each change since obviously the code will run on each change.
Since this is all happening in worksheet "WorkArea", there is no need to refer to it by its name i.e. you can safely use Range(rngAddress) and CommandButton1 instead of ThisWorkbook.Worksheets("WorkArea").Range(rngAddress) and ThisWorkbook.Worksheets("WorkArea").CommandButton1 respectively.
The Code
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Const rngAddress As String = "S11:T12"
Dim rng As Range
Set rng = Range(rngAddress)
If Not Intersect(Target, rng) Is Nothing Then
If WorksheetFunction.CountA(rng) = rng.Cells.Count Then
CommandButton1.Enabled = True
Else
CommandButton1.Enabled = False
End If
End If
End Sub
I have created a macro that allows a user to select an option from a dropdown list which unhides selected rows that corresponds with their selection. The problem is when I protect the sheet I get the following error "Run-time error '1004'" when I select an option for the drop down list. I need this sheet to be protected so the user cannot touch the data sets shown. Here is a sample code (original version is very long):
Private Sub Worksheet_Change(ByVal Target As Range)
ActiveSheet.Activate
If Not Application.Intersect(Range("C15"), Range(Target.Address)) Is Nothing Then
Select Case Target.Value
Case Is ="Option 1"
Rows("17:75").EntireRow.Hidden = True
Case Is ="Option 2"
Rows("17:28").EntireRow.Hidden = False
End Select
End If
End Sub
I've been reading other threads and I've come across a few options that said I need to Unprotect and Protect my sheet but I'm not to sure how to add this to the code above. And if this is the best option for what I am trying to accomplish
Option 1
Sub UnprotectAll()
Dim sh As Worksheet
For Each sh In ActiveWorkbook.Worksheets
sh.Unprotect Password:=yourPassword
Next sh
End Sub
Sub ProtectAll()
Dim sh As Worksheet
For Each sh In ActiveWorkbook.Worksheets
sh.Protect Password:=yourPassword
Next sh
End Sub
Option 2 - Adding this code somewhere below
UserInterFaceOnly:=True
Any suggestions in how I can accomplish this? And what the full code would look like?
Thanks so much!
#ExcelNoob I’ve made the following assumptions based on your question:
Only the active sheet is relevant
You formatted cell C15 as not Locked (when the sheet is unprotected, right click C15 /format/protection and uncheck ‘locked’ & ‘hidden’
You have indicated the correct rows you want hidden or not (seem a bit odd?)
There are only 2 options
That being the case, the minimum code below will do what you ask. If you want to use a specific password, just put it between the double quotation marks.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Range("C15"), Target) Is Nothing Then
ActiveSheet.Unprotect Password:=""
If Range("C15").Value = "Option 1" Then
Rows("17:75").Hidden = True
ElseIf Range("C15").Value = "Option 2" Then
Rows("17:28").Hidden = False
End If
ActiveSheet.Protect Password:=""
End If
End Sub
As per above, but if you don't want to protect/unprotect, put your drop down in a form and use :
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Sheet1.Protect "Password", UserInterfaceOnly:=True
End Sub
Where "password" is changed to whatever password you want.
I know this has been posted as a question numerous times. But I just can't get it working, I've tried numerous methods.
I have code that auto copies specific rows to a new sheet when a specific value is entered into Column B. But this only occurs when assign the marco to a button and manually trigger it. This isn't very efficient when copying over numerous rows. Especially when you're copying over hundreds of rows with only the last few actually changing. I'm hoping this will automatically happen when that value is entered.
So my first sheet is called MASTER and the second sheet is called CON. When Change of Numbers is entered into the MASTER I want to automatically copy these rows into sheet CON.
This code below is situated in The Master Sheet (which is the first). This script is used to hide/unhide specific Columns when values are entered into Column B.
MASTER SHEET
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B:B")) Is Nothing Then
On Error GoTo safe_exit
Application.EnableEvents = False
Dim t As Range
For Each t In Intersect(Target, Range("B:B"))
Select Case (t.Value)
Case "Change of Numbers"
Columns("B:BP").EntireColumn.Hidden = False
Columns("H:BL").EntireColumn.Hidden = True
'do nothing
End Select
Next t
End If
safe_exit:
Application.EnableEvents = True
End Sub
The following script is situated in sheet CON (which is the second sheet). This script is used to auto-copy the rows where X is entered into Column A in the Master sheet. However I have to assign this macro to a button on this sheet. It then grabs all the designated rows each time the macro is triggered.
CON SHEET
Option Explicit
Sub FilterAndCopy()
Dim sht1 As Worksheet, sht2 As Worksheet
Set sht1 = Sheets("MASTER")
Set sht2 = Sheets("CON")
sht2.UsedRange.ClearContents
With Intersect(sht1.Columns("B:BP"), sht1.UsedRange)
.Cells.EntireColumn.Hidden = False ' unhide columns
If .Parent.AutoFilterMode Then .Parent.AutoFilterMode = False
.AutoFilter field:=1, Criteria1:="Change of Numbers"
.Range("A:F, BL:BO").Copy Destination:=sht2.Cells(2, "B")
.Parent.AutoFilterMode = False
.Range("H:BK").EntireColumn.Hidden = True ' hide columns
End With
End Sub
But this still doesn't work without manually running the script.
Your code is not watching for any events to take place. The particular event you want is the Worksheet_Change() event, which is what I see in the second code snippet you provided.
So, you can go about this two ways. One, copy and paste the entire code into this event, or two (which is usually preferred) would be to call the sub within the event handler.
However, for the Worksheet to watch for the Change Event, you need to place this into the worksheet's code module. In the VBE, you will see this as Sheet1, Sheet2, etc.
My recommendation, place your Sub FilterAndCopy() in a standard module. Then in Sheet1's code module, add:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ErrHandler
'Test if criteria is met
If Intersect(Target, Columns("A")) Is Nothing Then
Exit Sub
ElseIf Target.Value = "mySpecificValue" Then
Application.EnableEvents = False
FilterAndCopy
Dim t As Range
For Each t In Intersect(Target, Range("a:a"))
Select Case UCase(t.Value)
Case "X"
Columns("B:C").EntireColumn.Hidden = True
Columns("D:E").EntireColumn.Hidden = False
Case "Y"
Columns("B:C").EntireColumn.Hidden = False
Columns("D:E").EntireColumn.Hidden = True
Case Else
'do nothing
End Select
Next t
End If
ErrHandler:
If Err.Number <> 0 Then
Rem: Optional - Error message and/or err recovery
End If
Application.EnableEvents = True
End Sub
If you first sub works exactly as intended all you need to do is Call the sub from your Worksheet_Change event. Just to be clear, as your Worksheet_Change macro is set-up, it will only call if the change is made on Column A
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Range("A:A")) Is Nothing Then Exit Sub
Application.EnableEvents = False 'to prevent endless loop
On Error GoTo Finalize 'to re-enable the events
FilterAndCopy
Finalize:
Application.EnableEvents = True
End Sub
Hej,
I've created a small VBA code to dynamically rename a worksheet.
It's working perfectly when the cell is just manually typed.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("C9")) Is Nothing Then
ActiveSheet.Name = ActiveSheet.Range("C9")
End If
End Sub
But then as soon as I will put a formula concatenating 2 cells values within C9 cell it will not update it automatically.
To make it work I need to enter the cell and type ENTER again and it works.
I have to do same manipulation each time I change a value in on of the 2 cell concatenated.
THANKS for your help guys
You need to capture a different event:
Private Sub Worksheet_Calculate()
Application.EnableEvents = False
ActiveSheet.Name = ActiveSheet.Range("C9")
Application.EnableEvents = True
End Sub
NOTE:
We disable events during the name change in case the worksheet contains a formula referencing the tab-name.
this should work:
replace
ActiveSheet.Name = ActiveSheet.Range("C9")
by
ActiveSheet.Name = ActiveSheet.Range("C9").Value
This is an alternate answer if someone still wants to execute this on worksheet change event
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
Dim formulacell As Range
Set formulacell = Range("C9")
Set formulacell = Application.Union(formulacell, formulacell.Precedents)
If Not Intersect(Target, formulacell) Is Nothing Then
ActiveSheet.Name = ActiveSheet.Range("C9").Value
End If
Application.EnableEvents = True
End Sub
Is there's any way to restrict the user from deleting the cell contents without using the protect method of excel. I have this code:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
If Not Intersect(Target, Range("C21:D" & ws.Range("C" & ws.Rows.Count).End(xlUp).Row)) Is Nothing Then
Cancel = True
MsgBox "You are not allowed to edit!", vbCritical + vbOKOnly
EndIf
End sub
But this only disallows the editing of the cell contents. I want make a function that would disallow the editing and deleting the data in a cell without using the protect method. Thanks!
Without lock and unlock, you can use this.
We have there one global variable to store selection value (to preserve beforechange state). Function SelectionChange, updating value of current cell, so we can restore cell value after users try.
Sub worksheet_change just controling, if user targeting specified row and column (can be adjusted for whole range), and if he try to change value, he is prompted and value is set back.
Dim prevValue As Variant
Private Sub worksheet_SelectionChange(ByVal target As Range)
prevValue = target.Value
End Sub
Private Sub worksheet_change(ByVal target As Range)
If target.Row = 5 And target.Column = 5 Then
If target.Value <> prevValue Then
target.Value = prevValue
MsgBox "You are not allowed to edit!", vbCritical + vbOKOnly
End If
End If
End Sub
edit: disable editing every cell which is not empty
Private Sub worksheet_change(ByVal target As Range)
If prevValue <> "" Then
If target.Value <> prevValue Then
target.Value = prevValue
MsgBox "You are not allowed to edit!", vbCritical + vbOKOnly
End If
End If
End Sub
Try my idea. Copy and paste these codes into the module of the sheet where the protected range is located. In my case it was called "Arkusz1". The protected range is "A1:A10".
Private Sub Worksheet_Change(ByVal Target As Range)
For Each cell In Range("A1:A10")
If cell.Text <> Sheets("hidden").Cells(cell.Row, cell.Column) Then
Call Undoing
End If
Next cell
End Sub
Private Sub Undoing()
Application.EnableEvents = False
Application.Undo
Application.EnableEvents = True
End Sub
In the "This_worksheet" module copy and paste this code:
Private Sub Workbook_Open()
Sheets.Add
ActiveSheet.Name = "hidden"
Sheets("Arkusz1").Range("A1:A10").Copy
Sheets("hidden").Select
ActiveSheet.Paste
Sheets("hidden").Select
ActiveWindow.SelectedSheets.Visible = False
End Sub
Private Sub Workbook_Close()
Sheets("hidden").Visible = True
Sheets("hidden").Delete
End Sub
You will have to change the name of the sheet from "Arkusz1" to "Sheet1" or any other name Your sheet has got.
The idea is as follows. Upon the opening of the workbook the application creates a hidden spreadsheet into which it copies the contents of protected cells. For some technical reasons I had to hide the sheet after the copy operation, otherwise it did not work on my computer. Then, any change of the sheet "Arkusz1" triggers the event which compares the contents of the protected range with the same range in the hidden sheet.
If there are any differences the application undoes the last action of the user.
Undoing has to be done when event handling by Excel is turned off, because undoing a previous action is also an event of changing the sheet and we would cause a cascade of events - every undo operation would trigger the event "worksheet_change" and it would never stop getting activated.
Application.EnableEvents = False
Application.Undo
Application.EnableEvents = True