How can I Lock/Unlock a cell(A1) based on value(yes/no) in another cell(A2) using conditional formatting?
There's lots of resources online showing how to do this. Here's an article that helped explain it to me:
http://database.ittoolbox.com/groups/technical-functional/excel-l/how-to-lockunlock-an-excel-cell-based-on-the-contents-of-another-cell-4625040
In case that link dies, here is the gist of it:
To do what you are describing, you will need to create an event procedure that Excel will call whenever the contents of the worksheet are changed. Start by opening the Visual Basic window (press Alt+F11). You should see s tree view in a pane at the top left; find the name of the worksheet in that view, and double-click the name. This will open the code module associated with that worksheet in the large pane on the right.
At the top of the large pane you will see two drop-down lists. Initially, the one on the left will display (General), while the one on the right will display (Declarations). Click the triangular arrowhead at the right end of the left list, and select Worksheet instead.
Excel will automatically add the "skeleton" of a SelectionChange event procedure. That's not what we want, but it won't hurt anything.
Excel will also change the selection in the right-hand drop-down list to SelectionChange. Open that list and select Change instead. Excel will add a second event procedure, which should look like this:
Private Sub Worksheet_Change(ByVal Target As Range)
End Sub
The blinking cursor will be positioned on the blank line of this skeleton.
You want to add code to the Worksheet_Change procedure to examine the contents of the G2 cell, and change the state of the G3:G66 range. This requires one IF statement, and a couple of assignment statements:
Private Sub Worksheet_Change(ByVal Target As Range)
If ActiveSheet.Cells(2, 7).Text = "X" Then
ActiveSheet.Range(Cells(3, 7), Cells(66, 7)).Locked = True
Else
ActiveSheet.Range(Cells(3, 7), Cells(66, 7)).Locked = False
End If
End Sub
The IF statement tests the current contents of cell G2 (the Cells() method takes a row -- 2 -- and a column number -- 7 -- to identify the cell; column G is the seventh column). The two assignment statements change the Locked property of the range; one locks the cells, the other unlocks them.
Of course, if your code were more complicated, you would want to avoid running it every time anything on the worksheet changes. To avoid executing the code too often, you can use the Target parameter that is passed to the event procedure:
If Intersect(ActiveSheet.Cells(2, 7), Target) _
Is Not Nothing Then
This conditional statement uses the Intersect() function to determine if cell G2 ( ActiveSheet.Cells(2, 7)) is included in Target.
Therefore, the complete event procedure would look like:
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(ActiveSheet.Cells(2, 7), Target) Is Not Nothing Then
If ActiveSheet.Cells(2, 7).Text = "X" Then
ActiveSheet.Range(Cells(3, 7), Cells(66, 7)).Locked = True
Else
ActiveSheet.Range(Cells(3, 7), Cells(66, 7)).Locked = False
End If
End If
End Sub
Related
I am trying to keep track of changes (to be appeared in a chart) upon every changes made in the worksheet using this reference (codes are copied below as example reference).
The changes in the worksheet actually can be made by several List Boxes (form Control Menu) and other cells e.g. Input 3 and 4. Let's say the resulting value caused by any change appears in the cell G14, and as the example code I want to keep them in I14 (as in the example image).
The formula might be e.g. D14 + E14 + K14 + L14 where K14 and L14 are values linked to the ListBoxes.
Selections in Input 3 and 4 trigger Worksheet_Change but selections in List Box apparently don't, shall I add a macro for List Box callback in which Worksheet_Change gets triggered?
Apart from that, if there is any better saving the results method, please mention it.
Many thanks in advance!
Dim xVal As String
Private Sub Worksheet_Change(ByVal Target As Range)
Static xCount As Integer
Application.EnableEvents = False
If Target.Address = Range("G14").Address Then
Range("I14").Offset(xCount, 0).Value = xVal
xCount = xCount + 1
Else
If xVal <> Range("G14").Value Then
Range("I14").Offset(xCount, 0).Value = xVal
xCount = xCount + 1
End If
End If
Application.EnableEvents = True
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
xVal = Range("G14").Value
End Sub
As you are asking about trigger event to run a macro on selection of listbox value (form control), I believe this could be considered as answer. I am sure someone with better knowledge will improve it.
Worksheet_Change event occurs when a cell or range of cells is changed manually (not by any auto process like calculation or selection of listbox value)
There can be different scenarios
First
There is no link between calculation of cells affected by the listbox change. Say, in First ListBox, you select 10, and cell A2 is updated to 10 and cell A3 is calculated value changed to 30. Then You select in second listbox 15 and cell B2 is changed to 15 and cell B3 is calculated independently if A2 and A3 to 90.In this case these are independent listboxes and their results. So, in this case, you can assign macro to each listbox which will run every time you change value in each listbox.
Second
Dependent calculations: Say the desired result for tracking is in D3 which will be calculated only on selection of four listbox values in A2, B2, C2 and D2. In this case you will not like to run macro for every list change but only after selecting values in all listboxes and calculation of D3. So in that case, instead of assigning macros to all the listboxes you could assign it only to last listbox.
Third
By now you must have understood there are two events taking place. One is selection of listbox value and second is calculation. In the second scenario, if you want to run macro for every change in calculation, say when you select A2 and when you select B2, etc. then you can simply use calculation event instead of assigning macro to every listbox. It will run everytime when a value is changed causing worksheet to calculate.
For assigning macro to listbox (form control) --- You can directly assign a macro to the list box.. First create a macro in VBA . Then Just right click on the list box and click assign macro. then select a macro to be assigned. .. The macro will be run when you click the listbox to change the value
Also, as you want to track the result calculated with macro, you need the sheet to be calculated first. Start the macro with Worksheet.Calculate method to be safe (in case formula results are not updated for some reason).
Tahnks to #Naresh, solved the problem in the following way, any improvement editis more than welcome since I know the codes might seem inefficient!
Dim xVal As String
Public Function customRecorder(Target As String)
Static xCount As Integer
Application.EnableEvents = False
Range("I14").Offset(xCount, 0).Value = xVal
xCount = xCount + 1
Application.EnableEvents = True
End Function
Private Sub Worksheet_Calculate()
xVal = Range("G14").Value
customRecorder (Range("G14"))
End Sub
I have the following situation: on one cell, I have a Data - validation - list which should restrict the user to selecting only items from that list.
On the same cell I have on change triggers in VBA, so when the cell value is changed it also records the change into a Changelog sheet (it uses undo to get the previous value, and undo again to redo the change to the new value)
The problem is, right now, the Data - validation - list is being totally ignored, so the users can put what ever they want in that specific cell, even though I have "Show error" checked.
Is there a way to enforce the list validation, so the users can only select items from the list and not enter whatever they want? Or to trigger the on cell change event after the validation?
Maybe someone can clarify the order in which these things happen.
I recently worked on range.Validation.type=xlValidateList and wanted to prevent paste and cut and disallow typing wrong characters into the cell containing the dropdown list(and making it possible to type characters that match an entry in the list and show that entry).
While working on the last thing that was not OK (cutting a dropdown and pasting it into another cell already containing a dropdown) I discoverd an EASY way of prohibiting paste and cut for Data Validation Lists that do allow further processing depending on what has been entered,
the following code has to be on the worksheet module.
It prevents the user from destroying Data Validation on cells and because of that using Application.Undo to revert to the state before pasting or cutting is no longer needed.
I don't know if it makes a difference but I protected the sheet and unlocked the cells the user may alter.
Private Function HasValidation(ByVal rng As Range) As Boolean
' See: https://superuser.com/questions/870926/restrict-paste-into-dropdown-cells-in-excel
' Returns True if every cell in Range r uses Data Validation
On Error Resume Next
Dim rngType: rngType = rng.Validation.Type
HasValidation = (Err.number = 0)
End Function
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' Switch DragAndDrop on/off on a range containing DataValidation
Dim modeCutCopy: modeCutCopy = Application.CutCopyMode
Dim usesValidation: usesValidation = HasValidation(Target)
If ((usesValidation = True) And (modeCutCopy <> 0)) Then Application.CutCopyMode = False ' MUST reset to avoid copying / cutting xlValidateList into another xlValidateList
' Turn DragAnddDrop on/off depending on if a cell uses Validation or not
If (usesValidation = Application.CellDragAndDrop) Then Application.CellDragAndDrop = (Not usesValidation)
GoTo SKIP
' Don't allow DragAndDrop if Target intersects with specified ranges
Dim myRange As Range
Set myRange = Worksheets("Evenementen").Range("Oordeel", "Waardering")
Dim rngIntersect As Range
Set rngIntersect = Application.Intersect(Target, myRange)
Application.CellDragAndDrop = (rngIntersect Is Nothing)
'' CheckInputValidationList(Target) ' Sophisticated testing and actions depending on the selection made
'' CheckInputValidationList(Target, myRange) ' Sophisticated testing and actions depending on the selection made for specific ranges
SKIP:
Debug.Print "SelectionChange " & Target.Address & " usesValidation=" & usesValidation & " cellDragAndDrop=" & Application.CellDragAndDrop
End Sub
In this case there are two named ranges containing (different) Data Validation Lists.
As soon as the user clicks on a cell a check will be performed if that cell uses data validation.
If so cut and paste will be turned off and CutCopyMode will be cleared until the user selects another cell.
Important is to retrieve the current CutCopyMode BEFORE any VBA-code will be executed that changes something as VBA will automatically change CutCopyMode from xlCut or xlCopy into 0 when something changes.
That initiall state is required to avoid pasting a dropdownlist over another.
The Worksheet_SelectionChange subroutine contains SKIPPED code that does something similar if a user accesses a cell in one of two ranges.
It also contains the (turned into comment) code to perform additional actions when the user selects a cell. That code might also be put in worksheet_Change
How might one turn spreadsheet cells into buttons, and execute VBA code, without inserting buttons, shapes or other active-X objects?
Using #TimWilliams' suggestion and referenced URL, based on readings there and further, I present some demonstration code, which works for me in Excel 2010: the IfError may not work in earlier versions, and I wonder if it behaves different in later versions.
Note: This code can not be debugged as one might with normal VBA. This is because it is executed on the "spreadsheet side", as a user defined function.
Place a formula in a cell (here, A2):
=IFERROR(HYPERLINK("#MyUDF()","CellText"),"Junk")
"CellText" will show in cell A2.
The "#...." points to the UDF. In combination with the Set statement in the code, it forces a "click" to execute, and only executes once on the click, rather than as a repeatable event executed when hovering and moving across the cell
The =IFERROR(HYPERLINK(...),...) is a workaround for the #name or other error., seen when using the mere =HYPERLINK(....).
Place this code in a module (a dimension and the UDF):
Dim j as integer
Function MyUDF() ' this is a user-defined-function
'NOTE: can't be traced when executed,
'so this creates debugging issues
Set MyUDF = Selection
Range("a1") = j
j = j + 1
End Function
Clicking on the URL in A2 will increment the value displayed in A1 - one increment per click.
To observe the hover effect:
Comment out the "Set Statement"
remove the quotes and pound sign around the UDF reference. The cell will now show "asdf" instead of "Test".
Roll across the URL, and cell A1 will increment as you move/hover.
To get the entire cell to cause execution of the UDF (and the incrementing value), turn on word wrap for the cell.
Notice how with the hover, the code is executed often as you move around in the cell. For me, the code does not execute if I stop moving while in the cell.
An Alternative to "putting a button in a cell"
Select a cell to execute code using the worksheet event: Worksheet_SelectionChange
Place the code in the specific sheet's module, not the WorkBook Module. Color/Border/Text the cell as you please; a cell-is-a-cell on any computer or screen. I use this to reference help loaded into a userForm, from a look up on a help worksheet, when the user clicks on a short label/description. This works on merged cells as buttons. Using cell/buttons avoids Active-X objects complaints from IT.
Things to think on, with the sample code, following:
Target.Address returns absolute addresses, using the "$" character
Use the Select Case in your code, even if you have one cell/button. This eases the path to adding cell/buttons, later
consider using named ranges on the spreadsheet, and reference them in the code. That way, VBA won't care if you move the cell/button
If you have merged cells for which you create a named range, remember that the named range, in the spreadsheet, only points to the top-left cell
However, the Target.Address for the merged area returns the full range, not just one cell. If your Select Case refers to the address of the Target's top-left cell, you avoid this problem.
use Target.Cells(1,1).Address
Bad choice for merged cells: don't use MergeArea.Address (MergeArea will not work on merged cells [only works on single cells]; it returns the merged range within which a cell lives.
*Sample Code*
'How to Make Cells into Buttons that execute code
' place code in the specific Worksheet module of interest
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' in this example, I create named ranges on the spreadsheet '
' [complicated names, here, so you can read what's what]:
' one cell: "Button_OneCellNameRange"
' one set of merged cells: "Button_MergedCellNameRange"
' [the reference is the top-left cell, only]
' a VBA created cell/button location [not very useful for later sheet edits]
Dim myVBACellButton As Range
Set myVBACellButton = Range("B2")
Debug.Print "Target Address: " & Target.Address
'merged cells will return a range: eg "$A$1:$D$3"
Debug.Print "Target.Cells(1,1).Address: " & Target.Cells(1, 1).Address
'merged cells will return the top left cell, which would match
' a named reference to a merged cell
Select Case Target.Cells(1, 1).Address
'if you have merged cells, you must use the ".cells(1,1).address"
' and not just Target.Address
Case Is = "$A$1"
MsgBox "Hello from: Click on A1"
' [execute a procedure/subroutine call, or more code, here...]
Case Is = myVBACellButton.Address
MsgBox "Hello from: Click on B2, a VBA referenced cell/button"
' "myCellButton" defined as range in VBA
'using a range named on the spreadsheet itself ...
' named ranges allow one to move the cell/button freely,
' without VBA worries
Case Range("Button_OneCellNameRange").Address
MsgBox "Hello From: Button Click on Button_OneCellNameRange"
Case Range("Button_MergedCellNamedRange").Address
'note that the address for merged cells is ONE CELL, the top left
MsgBox _
"Hello from: Button_MergedCellNamedRange.Address: " _
& Range("Button_MergedCellNamedRange").Address _
Case Else ' normally you wouldn't be using this, for buttons
MsgBox "NOT BUTTONS"
End Select
End Sub
I want to be able to run macros based on the outcome of the cell, for example.
If A1 is 1111100 then run X macro If its 1000000 then run this macro etc. I have had a look at "Case Select" but my lack of knowledge in this matter makes me thing that might not be what I want.
Any ideas? :/
Thank you in advanced.
JB
you can combine the two types, and yes, a Case Select is the easiest to read and maintain.
Here's example code that runs different routines depending on what is in A1:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Range("A1"), Target) Is Nothing Then
Application.EnableEvents = False
Select Case Target.Value
Case "A"
FunctionWhenA
Case 1
ThasIsAnotherFunction
End Select
Application.EnableEvents = True
End If
End Sub
note that I also disable/enable events so this isn't triggered every time a cell is changed
There are two main types of Excel macros
1- Those that are written to perform actions, change data, etc.
2- And those that are written to perform calculations and return values to certain cells
(used as customized formulas that are not built in to excel)
The first type can only be triggered to start execution by clicking a button on a form, invoking the Macros window within Excel and selecting the name of the macro to be run
The second type can be run as soon as a value of a certain cell changes (that cell must be an input for the said macro function to work with, calculate and return a certain output), and thus the returned value will be stored on another cell.
In the second type, Excel will ignore any code that tries to modify the content of other cells, perform actions on the worksheet, workbook, or any other action that is not limited to the cell contanining the formula for the custom macro.
If you intend to run a macro of the first type, and want it to be executed right after a certain value changes, then that is not possible.
If you want to write a macro of the second type, then that is possible but code will only be limited to a single cell only.
Yes would need to loop through them.
You could replace the "cas" subs in following with your implementation of VBA for that case.
Function Strange(myVal)
Select Case myVal
Case 1
Cas1
Case 2
Cas2
Case 3
Cas3
End Select
End Function
Sub Cas1()
MsgBox ("hi")
End Sub
Sub Cas2()
MsgBox ("ha")
End Sub
Sub Cas3()
MsgBox ("ho")
End Sub
Sub LoopThem()
Do While ActiveCell.Value <> ""
Strange (ActiveCell.Value)
ActiveCell.Offset(1, 0).Activate
Loop
End Sub
So cell A1 to A3 with values 1,2,3 would consecutively pop up msgboxes "hi" "ha" "ho".
Fixed it.
Done this via using Intergers to stop when I have no further cells with data to stop the macro, and detect on many '1's are within the code and copy the data as neededs using "For" commands.
I have some data validation drop down lists in excel, I can tab through all the lists but I have to press alt + down arrow to show the list, Is their a way it can be automatically shown as soon as I focus on the tab.
So on focus of the drop down list, I would like the list to appear So that I can select it with the arrow down key and hit enter to select it.
Any other helpful tips for drop down lists and VBA would be great!
edit: still using VBA send keys.
On the sheet where the data validation cell resides (assumed it is cells A1:C1 on Sheet1), put in the following code in the Microsoft Excel Sheet1 Module (the module that holds the VBA code that is related to the first sheet)
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error GoTo Err1:
If Target = Range("A1") Then
Application.SendKeys ("%{UP}")
End If
If Target = Range("B1") Then
Application.SendKeys ("%{UP}")
End If
If Target = Range("C1") Then
Application.SendKeys ("%{UP}")
End If
Err1:
'do nothing
End Sub
I found this helpful but would like to pass on a couple of observations.
Using
If Target.Cells.Count = 1 Then
If Target.Validation.InCellDropdown = True Then
in the event will apply this to all validation drop lists on the sheet rather than listing the individual cells. You need the first if to avoid an error caused by selecting multiple cells.
Beware of send keys being called twice in a row. It turns your num lock off and I had to add an API call to turn it back on again.
I hope this helps others