Excel VBA setting named cell formula using range property - vba

hoping you can help. Banging my head against the wall at this point. Fairly new to writing VBA. I have some VBA code listed below. Essentially, what I am trying to do is lock/unlock, change fill color, and set formula for a range of cells based on the input from a drop down menu using the worksheet change event and ActiveSheet. The code lives on the sheet itself. Everything works fine except for the formula setting piece.
Private Sub Worksheet_Change(ByVal Target As Range)
With ActiveSheet
.Unprotect Password:="somepw"
If Range("d17").Value = "Yes" Then
.Range("D22:D78").Locked = False
.Range("D22:D78").Interior.Color = RGB(115, 246, 42)
.Range("Inc_06PCTotRev").Formula = "=SUM($D$22:$D$25)"
ElseIf WorksheetFunction.CountA(Range("d22:D78")) <> 0 Then
If .Range("D22").Locked = True Then
With Range("D22:D78")
.Locked = False
.ClearContents
.Interior.Color = RGB(217, 217, 217)
End With
Else: .Range("D22:D78").ClearContents
End If
Else: .Range("D22:D78").Interior.Color = RGB(217, 217, 217)
.Range("D22:D78").Locked = True
End If
.Protect Password:="somepw"
End With
End Sub
When that is included I receive an error stating "method range of object _worksheet failed" and excel crashes. If I comment it out it fires without issue. Any help would be greatly appreciated. Please let me know if this isn't specific enough or doesn't make sense and I'll try my best to expand.

As I posted in my comments you are in a never ending loop because you change your worksheet in the worksheet change event. You need to keep track of the change call. Can alleviate this by creating a variable to keep track of when you called the change, this also is assuming that the name range is 1 cell.
Public bRunning As Boolean 'keeps track of when we are making the change
Private Sub Worksheet_Activate()
bRunning = False 'set to false when the sheet is activated
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
If bRunning = False Then 'check to see if this is the first change
bRunning = True 'set the variable letting us know we are making the change.
With ActiveSheet
.Unprotect Password:="somepw"
If Range("d17").Value = "Yes" Then
.Range("D22:D78").Locked = False
.Range("D22:D78").Interior.Color = RGB(115, 246, 42)
.Range("Inc_06PCTotRev").Formula = "=SUM($D$22:$D$25)"
ElseIf WorksheetFunction.CountA(Range("d22:D78")) <> 0 Then
If .Range("D22").Locked = True Then
With Range("D22:D78")
.Locked = False
.ClearContents
.Interior.Color = RGB(217, 217, 217)
End With
Else: .Range("D22:D78").ClearContents
End If
Else: .Range("D22:D78").Interior.Color = RGB(217, 217, 217)
.Range("D22:D78").Locked = True
End If
.Protect Password:="somepw"
End With
Else
bRunning = False 'reset the variable as we are done making changes
End If
End Sub

If you only need to perform a SUM when your routine is called, you can use the Worksheet.Sum function:
Application.WorksheetFunction.Sum(Range("$D$22:$D$25"))
If you actually need a formula in that cell, consider using the FormulaR1C1 function:
.Range("Inc_06PCTotRev").FormulaR1C1 = "=SUM(...)"
Lookup FormulaR1C1 in under Excel help to gain a better understanding of the R1C1 referencing syntax. If the cell you want to insert the formula in is $D$26, you would replace the "..." above with "R[-4]C:R[-1]C". If the formula is supposed to be in cell $E$20, you would replace the "..." above with "R[2]C[-1]:R[5]C[-1]".
R[#] references the # of rows from the destination range/cell and C[#] referenced the # columns from the destination range/cell.

Related

How do I move a row with a recently edited cell to bottom?

So far, I have an excel sheet that displays information about parts and in column 'H' there is an initial column that when someone puts their initials in that column it indicates that the part is finished. The row with the new initials should go to the bottom of the data. However, before this happens I have already set up a userform, 'UserForm2' in which the user will put in a password. So, if I could get some guidance on how to go about doing that when they press the 'OkayButton', that would be amazing!
Edit: I have tried moving it down with the worksheet change event, but I can't figure out how to get it to work.
Edit2(defunct): I have sort of figured it out; However, the code that I've indicated below I have added is giving me a invalid qualifier error.
Edit3: Some progress! The newly changed code below does what I it to now; However the 'userform2' keeps popping up after it has been copied to the bottom, I'm not entirely sure why and if anyone would happen to know how to fix it and could tell me that would be much appreciated!
Edit4: It works!... For the most part. The error that was in the previous edit is still popping up. Again, the updated code is below.
Userform2:
Private Sub CancelButton_Click()
Application.EnableEvents = False
Application.Undo
Application.EnableEvents = True
Unload Me
End Sub
Private Sub OkayButton_Click()
IniPass = "pass"
If Me.PasswordIn.Value = IniPass Then
Unload Me
Else
MsgBox "Incorrect Password"
End If
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = vbFormControlMenu Then
Cancel = True
MsgBox "Please use the Cancel button to close the password window!"
End If
End Sub
Sheet1(LookUp):
Private Sub Worksheet_Change(ByVal Target As Range)
LooupValue = Target.Value
part = Application.VLookup(LooupValue, MasterSheet.Range("A:AO"), 7, False)
desc = Application.VLookup(LooupValue, MasterSheet.Range("A:AO"), 9, False)
cust = Application.VLookup(LooupValue, MasterSheet.Range("A:AO"), 10, False)
due = Application.VLookup(LooupValue, MasterSheet.Range("A:AO"), 13, False)
If Not Application.Intersect(Range("A:A"), Target) Is Nothing Then
Range(Target.Address).Offset(0, 3).Value = part
Range(Target.Address).Offset(0, 4).Value = desc
Range(Target.Address).Offset(0, 5).Value = cust
Range(Target.Address).Offset(0, 6).Value = due
End If
If Not Intersect(Target, Range("H:H")) Is Nothing Then
UserForm2.Show
Application.EnableEvents = False
lastRow = Cells(Rows.Count, "A").End(xlUp).Row
Sheet1.Rows(Target.Row).Cut Sheet1.Rows(lastRow).Offset(1, 0)
Application.EnableEvents = True
Application.CutCopyMode = False
On Error Resume Next
Range("A:A").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End If
End Sub
Userform2 is probably popping up because this If Not Intersect(Target, Range("H:H")) Is Nothing Then is always evaluating to true. Does the range being passed into worksheet change always contain "H"?

VBA Macro Excecutes more than once

It's the first time I'm trying some VBA code, so it might be a very noob mistake but I just can't see it, this is my code:
Private Sub Worksheet_Change(ByVal Target As Range)
If InRange(ActiveCell, Range("N4:N33")) Then
If InStr(1, ActiveCell.Text, "EFECTIVO") > 0 Then
If (Not IsEmpty(ActiveCell.Offset(0, -1))) Then
If (ActiveCell.Offset(0, -1).Value > 0) Then
Cancel = True
Call RestaEfectivo
Range("F4").Select
End If
End If
End If
End If
End Sub
Function InRange(Range1 As Range, Range2 As Range) As Boolean
InRange = Not (Application.Intersect(Range1, Range2) Is Nothing)
End Function
Sub RestaEfectivo()
Range("F4").Value = Range("F4").Value - ActiveCell.Offset(0, -1).Value
End Sub
The idea is that I have a dropdown list on my cells N4 to N33, whenever I choose the option "EFECTIVO" it should take the value to the left of the ActiveCell (N#) and substract its value from the F4 cell. In essence F4 = F4 - N#.
The code does what it's supposed to do, however, it appears to execute 50 times? The original value of my F4 cell is 230, once I execute the code it turns into -20
Any idea where I messed up or if I'm missing some code, validation, etc?
As I said, I'm new to VBA for Excel Macros, so don't worry about pointing out noob mistakes.
You need to toggle the EnableEvents property of Application at the point where you call your RestaEfectivo sub-routine. Notice that during handling the Worksheet_Change event you call the RestaEfectivo sub-routine which fires the worksheet change event again - that is why your macro executes more than once.
You can make the code change like this:
Cancel = True
' turn off events to enable changing cell value without a new 'change' event
Application.EnableEvents = False
Call RestaEfectivo
' re-enable events to ensure normal application behaviour
Application.EnableEvents = True
Range("F4").Select
Update
OP asked a follow up question - how to make the range dynamic but ignore the bottom row as this would contain a SUM formula.
One possible solution is to check for the change in any cell of column N:
If InRange(ActiveCell, Range("N:N")) Then
And then recode the InRange sub - see the code comments for logic and assumptions:
Function InRange(Range1 As Range, Range2 As Range) As Boolean
Dim blnInRange As Boolean
Dim blnResult As Boolean
Dim blnCellHasSumFormula As Boolean
Dim blnCellIsEmpty As Boolean
'primary check for cell intersect
blnInRange = Not (Application.Intersect(Range1, Range2) Is Nothing)
If blnInRange Then
'follow-up checks
blnCellIsEmpty = (Range1.Value = vbNullString)
If blnCellIsEmpty Then
'cell in range but empty - assume beneath row with SUM
blnResult = False
Else
If Range1.HasFormula Then
'check for sum formula
blnCellHasSumFormula = (InStr(1, Range1.Formula, "SUM(", vbTextCompare) > 0)
If blnCellHasSumFormula Then
' cell in the SUM row
blnResult = False
Else
' cell is in range, not empty and not a SUM formula
blnResult = True
End If
Else
'assume non-empty cell without formula is good
blnResult = True
End If
End If
Else
blnResult = False
End If
'return to event handler
InRange = blnResult
End Function

Auto-hiding columns based on cell criteria in another worksheet

I am new to VBA coding and have so far successfully managed to create a scoping sheet in a workbook which hides/unhides tabs based on workbooks users' responses to yes/no questions.
I need to further refine the workbook so that the yes/no responses provided in the scoping tab lead to the auto hiding of columns in other sheets. Using a previous thread on this website I used this code (obviously amended for my own cells refs) on one of the tabs:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$B$6" Then
Select Case Target.Value
Case Is = "Cast"
Columns("f").EntireColumn.Hidden = False
Columns("d").EntireColumn.Hidden = True
Columns("e").EntireColumn.Hidden = True
Case Is = "LDF"
Columns("f").EntireColumn.Hidden = True
Columns("d").EntireColumn.Hidden = False
Columns("e").EntireColumn.Hidden = False
Case Is = "Select ROV Type"
Columns("f").EntireColumn.Hidden = False
Columns("d").EntireColumn.Hidden = False
Columns("e").EntireColumn.Hidden = False
End Select
In B6, I have a formula (=Name) which pulls through from the scoping tab. While the above code works, it only does so where I manually enter the cell to re-pull through data... any hints on:
- linking through to the original scoping tab in my macro, bypassing the cell reference; and
- automating the column hides?
The easiest thing to do seems to edit your code like this, where needed:
Sheet2.Columns("f").EntireColumn.Hidden = False
Sheet2 is the sheet, where the columns should be hidden.
If I correctly interpreted your needs go like follows
In "ThisWorkbook" code pane place the following code:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Application.EnableEvents = False
With Sheets("scoping sheet") '<== here you set which sheet you want to monitor
If .Range("B6") <> .Range("A1") Then '<== check if the "formula" cell changed its previous value, stored in the "echo" cell ("A1")
Select Case .Range("B6").Value
Case Is = "Cast"
.Columns("f").EntireColumn.Hidden = False
.Columns("d").EntireColumn.Hidden = True
.Columns("e").EntireColumn.Hidden = True
Case Is = "LDF"
.Columns("f").EntireColumn.Hidden = True
.Columns("d").EntireColumn.Hidden = False
.Columns("e").EntireColumn.Hidden = False
Case Is = "Select ROV Type"
.Columns("f").EntireColumn.Hidden = False
.Columns("d").EntireColumn.Hidden = False
.Columns("e").EntireColumn.Hidden = False
End Select
.Range("a1") = .Range("b6") '<== update the "echo" cell value for subsequent checking
End If
End With
Application.EnableEvents = True
End Sub
As you see, you must choose an "echo" cell in the "scoping" sheet, which will be used to store the previous value of its "B6" cell.
In my code I chose cell "A1" as the "echo" cell in "scoping" sheet, but you can choose whatever address you need provided it's a "free" cell (i.e.: your code and the user won't use it to write in) and change code accordingly (i.e. the "A1" address in If .Range("B6") <> .Range("A1") Then statement).

Show/Hide Rows Per Dropdown Selection

I found code online as an example that I have tweaked to show or hide specific rows depending on the selection I choose within a dropdown in my Excel file.
The macro is not working no matter what I try.
My code is as follows (also attached screenshot of rows under question 2 (2a - 2d) that are not showing/hiding)
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$F$13" Then
If Range("F13").Value = "Yes" Then
Rows("14:17").EntireRow.Hidden = False
End If
If Range("F13").Value = "No" Then
Rows("14:17").EntireRow.Hidden = True
End If
If Range("F13").Value = " " Then
Rows("14:17").EntireRow.Hidden = True
End If
End Sub
This is a good example of properly intending your code helping you identify an issue. You're missing an End IF statement. Try this:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$F$13" Then
If Range("F13").Value = "Yes" Then
Rows("14:17").EntireRow.Hidden = False
End If
If Range("F13").Value = "No" Then
Rows("14:17").EntireRow.Hidden = True
End If
If Range("F13").Value = " " Then
Rows("14:17").EntireRow.Hidden = True
End If
End If
End Sub
You may also want to use:
If Range("F13").Value = ""
instead of
If Range("F13").Value = " "
There is an End If missing. I assume the value of the target cell (F13) needs to be tested for it's value. If the value is "Yes", it should unhide row 14:17, if it is " " (spacebar) it should hide them and if it is "No" is should hide them as well. Other values will not affect the hiding/unhiding of the rows.
There should be a second End If before End Sub, so that all the if-statements above are wrapped within the Address check.
Also note that this code should be placed in the worksheet itself, since you want to hook into the Worksheet_Change event.
Try this in a worksheet module:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$F$13" Then 'Check if the changed value is indeed in F13
If Target.Value = "Yes" Then
ActiveSheet.Rows("14:17").EntireRow.Hidden = False 'Show the rows if the value is Yes
ElseIf Target.Value = "No" Then
ActiveSheet.Rows("14:17").EntireRow.Hidden = True 'Hide them when it's No
ElseIf Target.Value = " " Then
ActiveSheet.Rows("14:17").EntireRow.Hidden = True 'Or space
End If
End If
End Sub
Other remarks:
Instead of ActiveSheet you can also use Me (Me.Rows...) In this scenario they probably do the same. However, if you change the value on a worksheet from another worksheet (e.g. formula that recalculates), Me will reference the changed worksheet that fires the event, whereas activeworksheet will affect the currently active sheet.
Use Target instead of referencing the Range again. Target is a range object that is already in memory. Hence execution will be faster compared to accessing the worksheet again.

Dynamically protecting cells in a row based on "Y" Value in the Checked column?

I need to protect all the cells in a particular row if my user enters Y (yes) into a column of that particular row which indicates that the user has reviewed the data and that it is correct. I have not been able to figure out how to make this happen. Does anyone know how to do this?
Thanks so much,
Elias
As per your request and Byron's comments, I edited the code. The code should be pasted into the worksheet module
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
On Error GoTo Exiter
Set Sh = Target.Parent
If Target.Value = "Y" And Target.Column = 1 Then
Unprotect Password:="WHATEVER"
For Each curRow In Sh.UsedRange.Rows
If Sh.Cells(curRow.Row, 1) = "Y" Then
Sh.Cells(curRow.Row, 1).EntireRow.Locked = True
Else
Sh.Cells(curRow.Row, 1).EntireRow.Locked = False
End If
Next
Sh.Protect Password:="WHATEVER"
End If
Exiter:
Application.EnableEvents = True
End Sub