I am trying to write this VBA so that if cell(U5) = "YES" then range(B5:T5) are locked else range(B5:T5) remains unlocked. Similarly if cell(U6) = "YES" then range(B6:T6) are locked else range(B6:T6) remains unlocked and so on. Unfortunately my codes are not running and I am unable to find the error! Please find my mistake!
From your narrative, I've gathered that you only have to do anything if the values in column U are changed and then only to the cells in B:T on the rows where U has changed.
There is no indication as to what should happen if the cells in column U are neither Yes or No. I've left an area for you to deal with that if you want to.
You were using i + 21 in the column number parameter of cells. This means that the first time the loop goes through it is U5. On the second iteration it is now V6; on the third W7, etc. This is not what your narrative states so I followed the narrative's instructions and assumed a simple coding error. This also means that (if you had run through the code previously) you may have inadvertently 'locked' the wrong cells. If that is the case, you might just want to start on a new worksheet and delete the one you had been working on.
The Worksheet.Protect method has an optional parameter for UserInterfaceOnly. When set to True, the user is restricted by a protected worksheet but no VBA code is similarly protected. This means that you do not have to .Unprotect the worksheet to run your code. I've added a 'helper' sub procedure; run it once and then you do not have to Unprotect and reProtect the worksheet.
If you choose not to use UserInterfaceOnly protection, move the .Protect and .Unprotect statements outside the loop so that they are not performed for each row.
Related
We all know that Excel has some counter-intuitive behaviours and it is, I believe, one of them:
When you select a range of few cells, starting your selection with the cell with data validation list and choose value from drop-down list: only one cell changes (the one containing drop-down list) instead of all selected.
Sometimes a few magic keyboard shortcuts such as CTRL+d, or combination of CTRL+' and CTRL+ENTER can fix this behaviour, but from my experience clients doesn't like to learn some new hacks, they just want to work everything in as simple way as possible.
I found even similar questions on SO e.g. here:
Adding same drop-down value to multiple cells simultaneously
I know that this is very simple code, but following few lines of code make my life easier, and I am sure this will help somebody too. Code in Worksheet module of course:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
' MACRO FILLS THE WHOLE SELECTED RANGE
' WITH THE SAME VALUE USING DROP-DOWN LIST
' IN JUST ONE ACTIVE CELL
' change to false if all selected cells should be filled with value
Const FILL_VISIBLE_CELLS_ONLY As Boolean = True
' detecting if dropdown list was used
'
' I am using very clever solution by JvdV from SO
' ~~~~> stackoverflow.com/questions/56942551/
'
' If after edit we're in the same cell - drop-down list was used
' I know that may be also drag&drop or copy-paste
' but it seems no matters here.
' Warning! Should be add one more check if someone used
' 'accept OK character' next to formula bar, not implemented here.
'
If ActiveCell.Address <> Target.Address Then Exit Sub
' preventing error which sometimes occurs
If IsEmpty(ActiveCell.Value) Then Exit Sub
' fill a range or visible range with activeCell value
If FILL_VISIBLE_CELLS_ONLY Then
Selection.Cells.SpecialCells(xlCellTypeVisible) _
.Value = ActiveCell.Value
Else
Selection.Value = ActiveCell.Value
End If
End Sub
When you select a Range of more than one Cell, is is important to distinguish between the Active Cell (the single highlighted cell) and the Selection (the entire selected range, including the Active Cell).
Then:
Any Content (such as Values or Formulas) that you enter into the Formula bar is input into the Active Cell only, not the entire Selection.
Any Formatting changes you make are applied to the entire Selection, not just the Active Cell.
An exception is when entering an Array formula which applies to the Selection.
Since in this case you are making a change to content not formatting, it is applied to only the Active Cell.
When considering the above, it is not counterintuitive but entirely consistent with the design and operation of the software.
However, from a UX experience this may seem counterintuitive simply because it defies your expectation. This is kind of like a "customer is always right" type situation, which can be very frustrating for programmers, but is essential that it be understood. You can read more about the concept in a series of well-written articles on the topic at https://www.joelonsoftware.com/2000/04/10/controlling-your-environment-makes-you-happy/ (disclosure: the author of these articles happens to be integral to the development history of both Excel and SO, but it is linked here on merit). It was written more than 20 years ago and is still every bit as relevant today.
I have created a spreadsheet which uses the Worksheet_Change feature and the code associated with that works very well. I can stop it firing unnecessarily when inside the module by using Application.EnableEvents = False.
However, while I've created a form to enter data directly into the next available row (again, that works fine in terms of entry) it doesn't cause the formulae in the sheet to calculate (even though auto calculation is on and re-enabled within the module). If I manually place my cursor in the row, hit F2 and simply press enter, everything then fires up.
I have tried to enter data directly into the cells, but of course the Worksheet_Change feature then kicks in again and the cursor isn't simply moving to the next adjacent cell ....
I've tried to check firs for any direct entry with the code below and if it looks like the user isn't entering directly into the cell, the Worksheet_Change is disabled:
Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo eventhandler
Sheets(1).Range("a1").Select
LastCell2 = Sheets(1).Cells(Rows.Count, "A").End(xlUp).Row
Dim intersection As Range
Set intersection = Intersect(Target, Range("A3:F" & LastCell2))
If intersection.Row = LastCell + 1 Then
Exit Sub
End If
Application.EnableEvents = False
The code above is simply checking to see if data is being entered into the next empty cell and if that's the case I want it to just exit there but it isn't working.
So I actually have 2 problems :
the first is why this formula isn't triggering after entry via a vba form - I've used INDIRECT since there are other macros that delete rows by moving the remaining cells up and that was causing the count in the $A$3:$A$500 to reduce to $A$499 and then 498 etc - the addition is done depending on the system date and the transaction date so I get a current value and a future value using a standard sum statement:
=AD1-(SUMIF(INDIRECT("$A$3:$A$500"),"<="&TODAY(),INDIRECT("$E$3:$E$500")))+(SUMIF(INDIRECT("$A$3:$A$500"),"<="&TODAY(),INDIRECT("$F$3:$F$500")))
The second is why I can't enter data directly into the spreadsheet and trap the fact that I don't want it to do anything and simply allow the user to hit enter and move to the next adjacent cell to the one they just entered data into.
Is this a lost cause and am I trying to do too much here? I'm relatively new to coding and teaching myself so apologies if the standard and style isn't to everyone's taste.
Thanks in advance for any replies.
I have the following code to simulate some cells behave like buttons
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'Application.ScreenUpdating = False
If Target.Cells.Count = 1 Then
'~~~~~~ pseudocode ~~~~~~
If {select cell is one of the chose ones}
{do some stuff}
End If
'~~~~~~ pseudocode ~~~~~~
End If
Range("A1").Select
'Application.ScreenUpdating = True
End Sub
It doesn't matter whereas I use the ScreenUpdating code, the "Selection box" is flying around from A1 to the selected cell and back and it make the sheet much slower.
Can this flying (animation) selection box be stoped?
So far I have found this, not possible to hide, but noting about the flying efect:
Hide the cell selection box in Excel
Edit:
I need (I think so) edit capabilities on the sheet, therefore the option of not changing selection cell is not applicable. Due to:
most of the sheet is informative, and should be available for copy (not edited)
some cells are input forms (free text thing), selection as usual
some cells should behave like buttons (plus/minus for a numeric value, sclaes, simple stuff, but thousand of then, so much easier do/maintain by code), and user must not edit them
grouping should be available, (so that's complicate protecting the sheet)
I am not closed to the option : Range("A1").Select after each (most) of user interaction, but no other method comes into mind to me now.
An example:
I know some would say: "you should make this out from excel", and I agree with you, but this is a mandatory thing, I do not have the power to raise this question
As you can see, I got the "flying selection" that I try to get rid off
cell A1 is already hodden, that will do most of the trick
final version sure will go with hidden gridlines and headlines
rows groups exist, and are important, so no protection possible
all the functionality, I can do easy with vba, just problem with the animation
Maybe this is not the answer that you have been waiting for, but as Mathieu mentioned in his comment, please try to avoid using Selection.
It does make things slower and often causes errors (in example try selecting cell from hidden sheet). Instead just do something with the range that you define with your if statements directly. Every property of Cell or Range can be accessed directly.
Hope it helps.
Not sure how you can achieve you "flying select box" problem, but at least you could add this code, so opening/closing groups are available on protected sheets:
'Password Protect Current Sheet
ActiveSheet.Protect Password:="Add_here_your_password", UserInterfaceOnly:=True
'Enable Group Collapse/Expand Capabilities
ActiveSheet.EnableOutlining = True
How about trying to remove the "back to A1" as much as possible?
Maybe do it only on absolutely necessary, or move back to the changed value (the 33 in your example), or to the question title (in your multi-option example)
I'm trying to finish up a project of mine and I right now have a Form Control that when pressed adds to the value of a number and another button will subtract that value.
Another value has the two different buttons for the same thing, but the value is also dependent on the first value and other things than just the buttons modify that value. I tried implementing this code for validation
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("F19")) Is Nothing Then
If Range("E19") = 2 And Range("F19") < 12 Then
Range("E20") = 1
End If
End If
End Sub
but Excel apparently doesn't recognize that cell F19 has changed when the change is caused by the button, only when it is caused by user input. So, what this is saying is, if F19 updates and 19 is 2 and F19 is less than 12 (the prerequisite for E12 being 2 is F19 being 12 or greater) then set E20 to 1 (E20 is a modifier for E19 which also has other modifiers going into it). This method works on other values that aren't button controlled, but how can I get excel to realize when the Form Control button changes the value (or at least monitor when the form control is pressed.)
Edit: The macro actually doesn't work if the cell changes by formula either. I don't think I can use Worksheet_Calculate to monitor the change in a specific cell.
Why are you doing this with code anyway? You could have Cell E20 have a formula like: =IF(AND(E19=2,F19<12),1,"") which would make the cell blank unless the condition is met.
If you really want to do it with code, you should take this into account: The Worksheet_Change event "Occurs when cells on the worksheet are changed by the user or by an external link."
I would recommend instead of having
If Range("E19") = 2 And Range("F19") < 12 Then
Range("E20") = 1
End If
In your Worksheet_Change event that you add it as a separate sub, that you call from Worksheet_Change. You would also call the sub from the code for your button, after you've performed whatever action your button does. That way, you're guaranteed the check gets run and do not try to rely on events.
Daniel is partialy right, Worksheet_Change "Occurs when cells on the worksheet are changed by the user or by an external link." But this includes changes caused by VBA, and Excludes changes by a formula.
Your problem may be caused (or at least exacerbated) by the only partial qualification of your ranges:
Range("E19") will refer to 'E19' on the active sheet, which may or may not be be the sheets Target is on. You got it right with Target.Worksheet.Range("F19")
Try (note the .'s)
With Target.Worksheet
If Not Intersect(Target, .Range("F19")) Is Nothing Then
If .Range("E19") = 2 And .Range("F19") < 12 Then
.Range("E20") = 1
End If
End If
End With
BTW. I'm with Daniel in that your whole solution seems a little off, but we may not be getting the whole picture...
I have a pretty large Range (10,000 rows, 10 cols) that I fill line by line on an everyday basis. I also have a smaller Range (366 rows, 5 cols), in which, for every cell, I run a macro. That macro does pretty much what DSUM or SUMIF do, but with multiple criteria.
The problem is that after having this fancy stuff implemented, I can't fill my stuff decently, coz on every cell I fill the macro is ran dozens of times, and every times it loops hundreds, taking around 5 seconds to let me fill the next cell.
I'd like to be able to prevent the smaller range to be updated (or the macro not to be ran), so I can fill it all normally, and then press a button to update everything.
You will need to lock the target range (setting the Locked property of the Range to true). Then you will need to protect the sheet by calling the Protect() method of your worksheet. Below are examples of how to lock and unlock the cells A1:A4.
Locks:
Sheet1.Range("A1:A4").Locked = true
Sheet1.Protect()
Unlocks:
Sheet1.Unprotect()
Edit: After re-reading the question, it seems as though the OP is using the Worksheet_Change event. To which I have one word:
STOP
Just put in an ActiveX button into the sheet, and then assign a macro to that, as described here.
The Worksheet_Change event is fraught with peril. Beware ye who enter, for there be dragons.
Well there's this:
Application.Calculation = xlCalculationManual
But that stops all recalculations, until you rest it to automatic or explicity call for a Recalc. Is that sufficient, or do you need more?
If you need to limit the recalc restriction to only prevnting specific macros from executing, then the following:
Add a "Global IShouldNotRun as Boolean" to your macro(s)
Your macro(s) should check the IShouldNotRun value and exit if it is true.
write another macro that toggles this global.
Have whatever is doing you data loading call the routine to set the global to True.
Reset it after you are done loading and call Calculate, CalculateFull or CalculateFullRebuild.
I don't have ecell on my computer so I cannot check the exact path, but as far as I remember after going to Tools/Options menu there is a tab 'calculation' or 'computation' where you can disable the automatic formula recalculation.