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).
Related
I want to create a Select or Drop-down List or ComboBox (whatever you want to call it).
I do not want to do it with UserForm because it displays a window on the cells and I want it in the cells. Example (See image)
I want when selecting PERRO in the combobox the cell shows the number 1, if it is GATO the cell shows the number 2, if it is VACA the cell shows the number 3.
You have three options
Use Data Validation (DATA>DATA TOOLS>DATA VALIDATION) as you showed in the image, but you'll have to combine it with VLOOKUP function (kinda painful)
Attach a Combobox Form Control (DEVELOPER>CONTROLS>INSERT) right click and go to format control. From here, you can assign a source list and a result cell in numeric format. I mean if you select Perro, since it is the first element, the result cell give the value 1. It is the best way for me.
Finally, you can use a Combobox ActiveX Control (DEVELOPER>CONTROLS>INSERT), you can add the elements from the properties windows or VBA script by using the method AddItem
For example:
With ComboBox
.AddItem "perro"
.AddItem "gato"
.AddItem "vaca"
.AddItem "cerdo"
End With
and you can create a result cell with a conditional
Select Case Combobox.value
Case "perro": range("a1")=1
Case "gato": range("a1")=2
Case "vaca": range("a1")=3
Case "cerdo": range("a1")=4
End Select
That would be easy for a beginner.
other then the solution by the link provided by QHarr in the comments, you could try a totally VBA solution, with the code making the validation list appear every time the user selects the wanted cell
to do so you have to:
delete and placing back the validation rules at every cell selection
substitute list item select with its index in the list
so you need to deal with both Worksheet_SelectionChange() and Worksheet_Change() event handlers
place the following code in the worksheet code pane
Option Explicit
Dim animals As Variant
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$B$2" Then ' if selected cell is the wanted one (change "$B$2" to any wanted address
Dim element As Variant
Dim position As Long
For Each element In animals 'loop through validation list
position = position + 1 'update index position
If element = Target.Value Then 'if current loop element matches cell content
Application.EnableEvents = False 'prevent subsequent sheet change (deleting and writing cell content) fire this event again and start an infinite loop
Target.Validation.Delete 'remove data validation
Target.Value = position ' write the element index position
Application.EnableEvents = True 'restore proper event handling
Exit For
End If
Next
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Address = "$B$2" Then ' if selected cell is the wanted one (change "$B$2" to any wanted address
With Target
If hasValidation(.Cells) Then
.ClearContents 'clear any previous cell content
Else
animals = Array("Perro", "Gato", "Vaca", "Cerdo") 'set the validation list
.ClearContents 'clear any previous cell content
With .Validation 'set validation rules
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:=xlBetween, Formula1:=Join(animals, ",")
.IgnoreBlank = True
.InCellDropdown = True
.InputTitle = ""
.ErrorTitle = ""
.InputMessage = ""
.ErrorMessage = ""
.ShowInput = True
.ShowError = True
End With
End If
End With
End If
End Sub
Function hasValidation(rng As Range) As Boolean
On Error Resume Next
hasValidation = rng.SpecialCells(xlCellTypeSameValidation).Cells.Count = 1
End Function
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.
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.
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
I found the below code which I have modified slightly for my needs. The issue I'm having is it doesn't do exactly what I'd like. Specifically, I have a drop down menu in A1 of each sheet with the names of the three sheets, Shipping, Orders, and Inventory in my workbook. What I'm trying to accomplish is whenever a user selects a drop down menu item regardless of the sheet they are working in, the relevant sheet is shown and the other two are hidden.
The below code works, but only if all three sheets have the same sheet name in the drop down selected, which becomes untenable when two sheets get hidden. I'm not exactly sure how to overcome this, but hopefully someone here who is much better at this than I am will have some advice.
Current VB Code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Value = "Shipping" Then
Sheets("Shipping").Visible = True
Sheets("Orders").Visible = False
Sheets("Inventory").Visible = False
ElseIf Target.Value = "Orders" Then
Sheets("Orders").Visible = True
Sheets("Shipping").Visible = False
Sheets("Inventory").Visible = False
ElseIf Target.Value = "Inventory" Then
Sheets("Inventory").Visible = True
Sheets("Shipping").Visible = False
Sheets("Orders").Visible = False
End If
End Sub
Here is your code adapted for flexibility. This will hide any sheet that does not equal your target value, and unhide the sheet that DOES equal your target value.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet
Dim x As Worksheet
Set x = Excel.ActiveSheet
For Each ws In Excel.ActiveWorkbook.Worksheets
If Trim(ws.Name) <> Trim(Target.Value) and ws.Name <> x.Name Then
ws.Visible = xlSheetHidden
Else
ws.Visible = xlSheetVisible
End If
Next ws
End Sub
If you are wondering about the Trim() command, it removes leading and trailing spaces from the string value. Those are sometimes hard to spot in sheet names :)
Edit
I added the ws.Name <> x.Name part of the if statement to make sure the current sheet (aka the sheet on which the drop-down control is located) remains visible.
Start with all three sheets visible and use this code in all three sheets:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Value = "Shipping" Then
Sheets("Shipping").Visible = True
Sheets("Shipping").Select
Sheets("Orders").Visible = False
Sheets("Inventory").Visible = False
ElseIf Target.Value = "Orders" Then
Sheets("Orders").Visible = True
Sheets("Orders").Select
Sheets("Shipping").Visible = False
Sheets("Inventory").Visible = False
ElseIf Target.Value = "Inventory" Then
Sheets("Inventory").Visible = True
Sheets("Inventory").Select
Sheets("Shipping").Visible = False
Sheets("Orders").Visible = False
End If
End Sub