Hide rows in spreadsheet range containing a value - excel

I have a spreadsheet containing a list of all possible project tasks for different types of project in a range, and a column in the range which states to which project it relates.
In cell A1 I have a dropdown box of different project types - containing the values "Custom API" and "Custom File".
The data range is C3:E10, and example data is shown in the Example Data.
Column A: Task name
Column B: Task Duration
Column C: Task Owner
Column D: Project Type
What I'd like from some vba code is:
On selecting "Custom API" from the dropdown in A1, all the tasks in the range with the Project type "All" and "Custom API" to be shown, and all "Custom File" project task rows to be hidden.
On selecting "Custom File" from the dropdown in A1, all the tasks in the range with the Project type "All" and "Custom File" to be shown, and all "Custom API" project task rows to be hidden.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$1" and Target.Cells.Count = 1 Then
Application.ScreenUpdating = False
Range("B4:E10").EntireRow.Hidden = False
Dim taskList as Range
Set taskList = Range(Range("E4"),Range("E4").End(xlDown))
Dim taskCheck as Range
For each taskCheck in taskList
taskCheck.EntireRow.Hidden = taskCheck <> Target
Next
End If
End Sub

You are really just setting up an AutoFilter without header dropdowns.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$1" Then
Range("B4:E10").EntireRow.Hidden = False
If AutoFilterMode Then AutoFilterMode = False
With Range(Cells(3, "E"), Cells(4, "E").End(xlDown))
.AutoFilter field:=1, Criteria1:=Array(Cells(1, "A").Value, "All"), _
Operator:=xlFilterValues, VisibleDropDown:=False
End With
End If
End Sub
You can clear the AutoFilter and show all values by adding an asterisk (e.g. *) to your list of values for the A1 dropdown.

Please try this code. Make sure that the spelling of the items in A1 match with that in the test column.
Private Sub Worksheet_Change(ByVal Target As Range)
' 03 Jan 2019
' set these two constants to match your sheet
Const FirstDataRow As Long = 4
Const TestClm As String = "E"
Dim Rng As Range
Dim Arr As Variant
Dim Tgt As String
Dim C As Long
Dim R As Long
' (If the address is $A$1 it can't have more than one cell)
If Target.Address = "$A$1" Then
Tgt = Target.Value
Rows.Hidden = False
C = Columns(TestClm).Column
Set Rng = Range(Cells(FirstDataRow, C), Cells(Rows.Count, C).End(xlUp))
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
With Rng
Arr = .Value
For R = 1 To UBound(Arr)
Rows(R + FirstDataRow - 1).Hidden = Not (CBool(StrComp(Arr(R, 1), Tgt, vbTextCompare) = 0) Or _
CBool(StrComp(Arr(R, 1), "All", vbTextCompare) = 0))
Next R
End With
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End If
End Sub

Related

Dropdown list circular reference Excel/VBA

I am following up on an answer that has been posted before at the following link: Circular Reference with drop-down list
The answer works when the dropdown lists and sources are on the same cell on their respective sheets, but I am trying to find out how this work if the lists and source are not on the same cell. Thank you
I am following this answer:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Target.Address = "$B$5" And Sh.Name <> "Sheet3" Then
On Error GoTo bm_Safe_Exit
Application.EnableEvents = False
Dim w As Long
For w = 1 To Worksheets.Count
With Worksheets(w)
'skip this worksheet and Sheet3
If CBool(UBound(Filter(Array(Sh.Name, "Sheet3"), _
.Name, False, vbTextCompare))) Then
.Range("B5") = Target.Value
'.Range("B5").Interior.ColorIndex = 3 '<~~testing purposes
End If
End With
Next w
End If
bm_Safe_Exit:
Application.EnableEvents = True
End Sub
I am trying to have two lists where I can change one and it'll update the other. How do I create the same result in the dropdown for example on cell A3 on Sheet1 and D9 on Sheet2?
Here is what I am after: I want to generate on two sheets (sheet 1, sheet 2) a drop-down list that says either "Complete" or "Incomplete." If I change sheet 1 from Complete to Incomplete, I want sheet 2 to say the same thing, but I also want vice versa
(If I change sheet 2 from Complete to Incomplete, I want sheet 1 to change).
Try like this:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim arrCells, el, i As Long, m, tgt, arr
arrCells = Array("Sheet1|D3", "Sheet2|B4") 'all cells with the list
tgt = Sh.Name & "|" & Target.Address(False, False)
m = Application.Match(tgt, arrCells, 0) 'matches one of the list cells?
If Not IsError(m) Then
On Error GoTo bm_Safe_Exit
Application.EnableEvents = False
For i = LBound(arrCells) To UBound(arrCells)
If arrCells(i) <> tgt Then 'skip the cell raising the event...
arr = Split(arrCells(i), "|")
ThisWorkbook.Sheets(arr(0)).Range(arr(1)).Value = Target.Value
End If
Next i
Application.EnableEvents = False
End If
bm_Safe_Exit:
Application.EnableEvents = True
End Sub

Auto-Updated Validated Cell When Source Value Changes

I'm trying to update cells that have data validation restrictions on them automatically.
For example - Sheet1 has below column (Column E):
Package Identifier
A
B
C
where the values are taken from the same named column (Column D) in Sheet2.
The below code works for MANUAL changes only
Sheet2 Code:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.ScreenUpdating = False
Dim count_cells As Integer
Dim new_value As String
Dim old_value As String
Dim rng As Range
For count_cells = 1 To Range("D1").CurrentRegion.Rows.Count - 1
Set rng = Worksheets("Sheet1").Range("E3:E86")
If Intersect(Target, Range("D" & count_cells + 1)) Is Nothing Then
Else
Application.EnableEvents = False
new_value = Target.Value
Application.Undo
old_value = Target.Value
Target.Value = new_value
rng.Replace What:=old_value, Replacement:=new_value, LookAt:=xlWhole
Target.Select
End If
Next count_cells
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
So, if i manually change value B to Z, all the corresponding values that were B on Sheet1 now change to Z. The problem is, Package Identifier on Sheet2 is dictated by concatenating other columns
=CONCATENATE(B35, "-", "Package", "-", TEXT(C35, "#000"))
This piece of code breaks when trying to use it with the above formula. How can i make this set of code trigger on this formula based output?
Assuming this is how the Validation sheet looks
and this is how the Source sheet looks
Let's say user selects first option in Validation sheet.
Now go back to Source sheet and change 1 to 2 in cell C2.
Notice what happens in Validation sheet
If this is what you are trying then based on the file that you gave, test this code.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim aCell As Range
Dim NewSearchValue As String
Dim OldSearchValue As String
Dim NewArrayBC As Variant
Dim OldArrayA As Variant, NewArrayA As Variant
Dim lRow As Long, PrevRow As Long
On Error GoTo Whoa
Application.EnableEvents = False
If Not Intersect(Target, Range("B:C")) Is Nothing Then
lRow = Range("A" & Rows.Count).End(xlUp).Row
'~~> Store new values from Col A, B and C in an array
NewArrayBC = Range("B1:C" & lRow).Value2
NewArrayA = Range("A1:A" & lRow).Value2
Application.Undo
'~~> Get the old values from Col A
OldArrayA = Range("A1:A" & lRow).Value2
'~~> Paste the new values in Col B/C
Range("B1").Resize(UBound(NewArrayBC), 2).Value = NewArrayBC
'~~> Loop through the cells
For Each aCell In Target.Cells
'~~> Check if the prev change didn't happen in same row
If PrevRow <> aCell.Row Then
PrevRow = aCell.Row
NewSearchValue = NewArrayA(aCell.Row, 1)
OldSearchValue = OldArrayA(aCell.Row, 1)
Worksheets("Validation").Columns(2).Replace What:=OldSearchValue, _
Replacement:=NewSearchValue, Lookat:=xlWhole
End If
Next aCell
End If
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
A different approach from Sid's...
Instead of updating values in the DV cells when the source range changes, this replaces the selected value with a link to the matching cell in the DV source range.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngV As Range, rng As Range, c As Range, rngList As Range
Dim f As Range
On Error Resume Next
'any validation on this sheet?
Set rngV = Me.Cells.SpecialCells(xlCellTypeAllValidation)
On Error GoTo 0
If rngV Is Nothing Then Exit Sub 'no DV cells...
Set rng = Application.Intersect(rngV, Target)
If rng Is Nothing Then Exit Sub 'no DV cells in Target
For Each c In rng.Cells
If c.Validation.Type = xlValidateList Then 'DV list?
Set rngList = Nothing
On Error Resume Next
'see if we can get a source range
Set rngList = Evaluate(c.Validation.Formula1)
On Error GoTo 0
If Not rngList Is Nothing Then
Application.EnableEvents = False
'find cell to link to
Set f = rngList.Find(c.Value, LookIn:=xlValues, lookat:=xlWhole)
If Not f Is Nothing Then
Application.EnableEvents = False
c.Formula = "='" & f.Parent.Name & "'!" & f.Address(0, 0)
Application.EnableEvents = True
End If
Else
Debug.Print "No source range for " & c.Address
End If
End If
Next c
End Sub

Keep column creation to a limit in VBA

I have the following VBA code I compiled based on some stuff I found online.
It works (mostly) as I intend it to, as on change in column H it creates a new column to the right of the last column, in the active row, with the latest value.
However, I'd like, if possible, the following:
to keep only the latest 5 changes;
currently, it somehow always ignores the fact that the last column of the active row is empty and just creates a new column after so my updates always look like this, even if each of them is the first update for each row (I can expand if this wasn't clear enough).
Here's the VBA code I have now:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("H:H")) Is Nothing Then
ActiveCell.Offset(-1, 0).Activate
a = Sheets("SORTIES").Cells(ActiveCell.Row, Columns.Count).End(xlToLeft).Column + 1
Column = Split(Cells(1, a).Address, "$")(1)
Sheets("SORTIES").Range(Column & ActiveCell.Row).Value = ActiveCell.Value
ActiveCell.Offset(0, 0).Select
End If
End Sub
Please, try the next code. It determines the last empty column inside the table and uses a Static dictionary variable, to keep records for all the cells in the table, for H:H column. It needs a reference to "Microsoft Scripting Runtime". To add this reference you need to be in VBE (Visual Basic for Applications Editor) and go Tools (menu) -> References..., scroll down until find "Microsoft Scripting Runtime" library, check it and press OK.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Static dictSt As New Dictionary 'it needs a reference to "Microsoft Scripting Runtime"
If Target.cells.count > 1 Then Exit Sub
If Target.Address = "$E$1" And Target.Value = "x" Then dictSt.RemoveAll: Target.Value = "": Exit Sub 'clear the dictionary
If Not Intersect(Target, Range("H:H")) Is Nothing Then
Dim lstO As ListObject: Set lstO = ActiveSheet.ListObjects(1) 'use your table namem if there is not only one
If dictSt.count = 0 Then 'load the dictionary for first time
'load the dictionary referenced cells
Dim arrRng As Range, i As Long, cel As Range
Set arrRng = Intersect(lstO.DataBodyRange, Range("H:H"))
For Each cel In arrRng.cells
dictSt(cel.Address) = 0
Next
End If
If Not Intersect(Target, lstO.DataBodyRange) Is Nothing Then
Dim lastEmptRng As Range
Set lastEmptRng = cells(lastListCol(Target, lstO)(0), lastListCol(Target, lstO)(1))
'operate the change in the dictionary and use the last history value after 5 records
If dictSt(Target.Address) >= 5 Then
Dim arrVal As Variant, arrUpdate As Variant, El
arrVal = Range(Target.Offset(0, 1), cells(Target.row, lastEmptRng.Column - 1)).Value
ReDim arrUpdate(1 To 1, 1 To UBound(arrVal, 2))
For i = 1 To UBound(arrVal, 2) - 1
arrUpdate(1, i) = arrVal(1, i + 1)
Next
arrUpdate(1, 5) = Target.Value
With Application
.EnableEvents = False
.Calculation = xlCalculationManual
Range(Target.Offset(0, 1), cells(Target.row, lastEmptRng.Column - 1)).Value = arrUpdate
.Calculation = xlCalculationAutomatic
.EnableEvents = True
End With
Else
lastEmptRng.Value = Target.Value
dictSt(Target.Address) = dictSt(Target.Address) + 1
End If
Target.Select
End If
End If
End Sub
Private Function lastListCol(tg As Range, lstO As ListObject) As Variant
'this piece of code search the last empty column (of the Target row) inside the table!
Dim listRow As Long, fRng As Range
listRow = tg.row - lstO.DataBodyRange.row + 1
Set fRng = lstO.DataBodyRange.rows(listRow).Find(What:="*", _
After:=lstO.DataBodyRange.cells(listRow, 1), LookIn:=xlValues, _
searchorder:=xlByColumns, SearchDirection:=xlPrevious)
If Not fRng Is Nothing Then
lastListCol = Array(tg.row, fRng.Column + 1) ' Stop
Else
lastListCol = Array(tg.row, lstO.DataBodyRange.cells(listRow, 1).Column)
End If
End Function
The dictionary can be reset by typing "x" in cell "E1"!
Edited:
Now, it keeps the last records for all recording history.

Application.Goto Target Cell Not in View

I have created a simple Excel Macro which is triggered when a user clicks on a cell in a worksheet (worksheet1). Basically the macro takes the value of the cell which was clicked on and selects a target cell in a separate worksheet (worksheet2) that has the same value.
The problem is that about 20% of the time after being directed to worksheet2, the target cell is highlighted but is just out of view, i have to scroll down a couple of rows to see it. I want to be able to ensure that the target cell is always in view after the user is directed to it, but I am not sure how this can be achieved.
This is in Excel 2016.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If ActiveCell.Column = 1 Then
If Target.Cells.Count = 1 Then
Application.ScreenUpdating = False
Dim c As Range
Dim ans As String
Dim Lastrow As Long
ans = ActiveCell.Value
Lastrow = Sheets("worksheet2").Cells(Rows.Count, "A").End(xlUp).Row
For Each c In Sheets("worksheet2").Range("A2:A" & Lastrow)
If c.Value = ans Then Application.Goto Reference:=Sheets("worksheet2").Range(c.Address): Exit Sub
Next
End If
End If
Exit Sub
End Sub
You can use find to find the selected item in sheet2 then just select the sheet and the found cell
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim s As Range
If Target.Column = 1 Then
Set s = Worksheets("Sheet2").Range("B:B").Find(what:=Target, lookat:=xlWhole)
If Not s Is Nothing Then
Worksheets("Sheet2").Activate
s.Select
Else: MsgBox Target.Value & " is not found in sheet 2"
End If
End If
End Sub

Hide Multiple Sheets if Value in Column is No (50+Columns/Sheets) Using Loop

I have a master sheet (Sheet 1) that contains 50+ rows of specific items. I have a sheet corresponding to each item and named as such (ie. item 1 = "Clearing" so sheet 2 is named "Clearing"). I have a drop down menu for each item in Column D that displays "Yes" or "No".
I currently have a basic code that hides Sheets based on if my "Column D" drop down menus for 50+ rows = "No" (ie. Item 1 marked as "No" so sheet 2 is hidden).
Private Sub Worksheet_Change(ByVal Target As Range)
If [D2] = "Yes" Then
Sheets("Clearing").Visible = True
Else
Sheets("Clearing").Visible = False
End If
If [D3] = "Yes" Then
Sheets("Grubbing").Visible = True
Else
Sheets("Grubbing").Visible = False
End If
End Sub
I want to be able to run this in a loop for all 50+ items by using a range of cells D2:D50+ without having to enter in each sheet name as I've done above. I haven't been able to figure out how to manage this by looking at other's examples.
Any help is much appreciated.
Using the Worksheet_Change event you started out with:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Set rng = Range(Range("D2"), Range("D2").End(xlDown))
If Not Intersect(Target, rng) Is Nothing Then
If Target.Count = 1 Then
On Error GoTo ErrorHandler
If Target = "Yes" Then
Sheets(Target.Offset(, -1).Value).Visible = True
Else
Sheets(Target.Offset(, -1).Value).Visible = False
End If
End If
End If
Exit Sub
ErrorHandler:
MsgBox "The sheet '" & Target.Offset(, -1) & "' does not exist!"
End Sub
If your data is set up with the sheet name next to column D (or anywhere really, just adjust the script), you can just loop through.
Sub hide_Sheets()
Dim mainWS As Worksheet
Dim rng As Range
Set mainWS = ThisWorkbook.Sheets("Sheet1")
Set rng = mainWS.Range("C2:C5") ' Change range as needed
Dim cel As Range
For Each cel In rng
If cel.Offset(0, 1).Value = "Yes" Then
ThisWorkbook.Sheets(cel.Value).Visible = True
Else
ThisWorkbook.Sheets(cel.Value).Visible = False
End If
Next cel
End Sub

Resources