Dynamically Hide/Unhide Multiple Ranges Using VBA With Minimal Lag - excel

I am trying to dynamically hide or unhide rows in a worksheet based off of selections within dropdown menus.
The script that I have works with smaller data sets, but because I have 35 different ranges of 26 rows each this slows down really quickly.
I have seen several solutions offered to similar question here, but I have been unable to get them to work.
I want to collect the value in cells B15 down to B41 and hide any rows that have a blank value, then repeat for the remaining 34 ranges.
Each of the cells in the range above have a formula that can return a "" value (which are the rows I want to hide).
Private Sub Worksheet_Change(ByVal Target As Range)
'Turns off worksheet protection to allow for hiding and unhiding of rows
ActiveSheet.Unprotect "xxxx"
'Turns off screen updating and animations while hiding and unhiding rows
Application.EnableAnimations = False
Application.ScreenUpdating = False
Hide1
Hide2
Hide3
Hide4
Hide5
Hide6
Hide7
Hide8
Hide9
Hide10
Hide11
Hide12
Hide13
Hide14
Hide15
Application.ScreenUpdating = True
Application.EnableAnimations = True
ActiveSheet.Protect "xxxx"
End Sub
Sub Hide1()
Application.EnableEvents = False
Application.EnableAnimations = False
Application.ScreenUpdating = False
'Run #1
If Range("B15").Value = "" Then
Rows(15).EntireRow.Hidden = True
Else
Rows(15).EntireRow.Hidden = False
End If
If Range("B16").Value = "" Then
Rows(16).EntireRow.Hidden = True
Else
Rows(16).EntireRow.Hidden = False
End If
If Range("B17").Value = "" Then
Rows(17).EntireRow.Hidden = True
Else
Rows(17).EntireRow.Hidden = False
End If
If Range("B18").Value = "" Then
Rows(18).EntireRow.Hidden = True
Else
Rows(18).EntireRow.Hidden = False
End If
If Range("B19").Value = "" Then
Rows(19).EntireRow.Hidden = True
Else
Rows(19).EntireRow.Hidden = False
End If
If Range("B20").Value = "" Then
Rows(20).EntireRow.Hidden = True
Else
Rows(20).EntireRow.Hidden = False
End If
If Range("B21").Value = "" Then
Rows(21).EntireRow.Hidden = True
Else
Rows(21).EntireRow.Hidden = False
End If
If Range("B22").Value = "" Then
Rows(22).EntireRow.Hidden = True
Else
Rows(22).EntireRow.Hidden = False
End If
If Range("B23").Value = "" Then
Rows(23).EntireRow.Hidden = True
Else
Rows(23).EntireRow.Hidden = False
End If
If Range("B24").Value = "" Then
Rows(24).EntireRow.Hidden = True
Else
Rows(24).EntireRow.Hidden = False
End If
If Range("B25").Value = "" Then
Rows(25).EntireRow.Hidden = True
Else
Rows(25).EntireRow.Hidden = False
End If
If Range("B26").Value = "" Then
Rows(26).EntireRow.Hidden = True
Else
Rows(26).EntireRow.Hidden = False
End If
If Range("B27").Value = "" Then
Rows(27).EntireRow.Hidden = True
Else
Rows(27).EntireRow.Hidden = False
End If
If Range("B28").Value = "" Then
Rows(28).EntireRow.Hidden = True
Else
Rows(28).EntireRow.Hidden = False
End If
If Range("B29").Value = "" Then
Rows(29).EntireRow.Hidden = True
Else
Rows(29).EntireRow.Hidden = False
End If
If Range("B30").Value = "" Then
Rows(30).EntireRow.Hidden = True
Else
Rows(30).EntireRow.Hidden = False
End If
If Range("B31").Value = "" Then
Rows(31).EntireRow.Hidden = True
Else
Rows(31).EntireRow.Hidden = False
End If
Application.EnableEvents = True
Application.EnableAnimations = True
Application.ScreenUpdating = True
End Sub

Please, try the next code. As it is set, it will hide all rows having empty values returned by a formula. firstRand lastR can be chosen to process a specific number of rows:
Sub Hide1()
Dim sh As Worksheet, lastR As Long, firstR As Long
Dim rng As Range, rngH As Range, arr, i As Long
Set sh = ActiveSheet 'use here the sheet you need
lastR = sh.Range("B" & sh.rows.Count).End(xlUp).row 'last row on B:B
firstR = 15 'first row of the range to be processed
Set rng = sh.Range("B" & firstR & ":B" & lastR)
rng.EntireRow.Hidden = False 'show all rows in the range
arr = rng.Value 'place the range in an array for faster iteration
For i = 1 To UBound(arr)
If arr(i, 1) = "" Then
If rngH Is Nothing Then 'set the range to keep the cells where the rows must be hidden
Set rngH = rng.cells(i, 1)
Else
Set rngH = Union(rngH, rng.cells(i, 1))
End If
End If
Next
'hide the rows at once:
If Not rngH Is Nothing Then rngH.EntireRow.Hidden = True
End Sub

Hide Blank Rows
Adjust the values in the constants section.
Option Explicit
Sub HideBlankRows()
Const fCellAddress As String = "B16"
Const crgCount As Long = 35
Const crgSize As Long = 16 ' maybe 26 ?
Const crgGap As Long = 5
Dim ws As Worksheet: Set ws = ActiveSheet
Dim crg As Range: Set crg = ws.Range(fCellAddress).Resize(crgSize)
Dim crgOffset As Long: crgOffset = crgSize + crgGap
Dim rg As Range: Set rg = crg
Dim n As Long
For n = 2 To crgCount
Set crg = crg.Offset(crgOffset)
Set rg = Union(rg, crg)
Next n
Dim drg As Range
Dim cCell As Range
For Each cCell In rg.Cells
If Len(CStr(cCell.Value)) = 0 Then
If drg Is Nothing Then
Set drg = cCell
Else
Set drg = Union(drg, cCell)
End If
End If
Next cCell
If drg Is Nothing Then Exit Sub
rg.EntireRow.Hidden = False
drg.EntireRow.Hidden = True
End Sub

Related

VBA Merge Similar Cells

I would like to merge similar cells by columns, as of now I am using this macro
Sub MergeSimilarCells()
Set myRange = Range("A1:Z300")
CheckAgain:
For Each cell In myRange
If cell.Value = cell.Offset(0, 1).Value And Not IsEmpty(cell) Then
Range(cell, cell.Offset(0, 1)).Merge
cell.VerticalAlignment = xlCenter
cell.HorizontalAlignment = xlCenter
GoTo CheckAgain
End If
Next
End Sub
My problem is with hundreds of rows and 40-50 columns, it takes forever.
I am pretty sure a For Loop could help me there but I am not skilled enough to figure it out
I know the following code is wrong but I am lost
Sub SimilarCells()
Set myRange = Range("A1:G4")
Dim count As Integer
CheckAgain:
count = 1
For Each cell In myRange
If cell.Value = cell.Offset(0, 1).Value And Not IsEmpty(cell) Then
count = count + 1
ElseIf cell.Value <> cell.Offset(0, 1).Value Then
Range(cell, cell.Offset(0, -count)).Merge
End If
Next
End Sub
Here is what I would like to accomplish
Sub MergeMe()
Dim wks As Worksheet: Set wks = Worksheets(1)
Dim myRange As Range: Set myRange = wks.Range("B2:H5")
Dim myCell As Range
Dim myCell2 As Range
Dim firstColumn As Long: firstColumn = myRange.Columns(1).column + 1
Dim lastColumn As Long: lastColumn = firstColumn + myRange.Columns.Count - 1
Dim firstRow As Long: firstRow = myRange.Rows(1).row
Dim lastRow As Long: lastRow = firstRow + myRange.Rows.Count - 1
Dim column As Long
Dim row As Long
OnStart
For column = lastColumn To firstColumn Step -1
For row = lastRow To firstRow Step -1
Set myCell = wks.Cells(row, column)
Set myCell2 = myCell.Offset(0, -1)
If myCell.Value = myCell2.Value Then
With wks.Range(myCell, myCell2)
.Merge
.VerticalAlignment = xlCenter
.HorizontalAlignment = xlCenter
End With
End If
Next row
Next column
OnEnd
End Sub
There are quite a few tricks in this code:
we need to get the first and last column and row;
then we should be looping from the last cell (bottom right) to the first one (top left);
we should not enter the first column, because we are using .Offset(0,-1) and we compare every cell with its leftmost one;
the reason for the whole operation, is that by default, the value of a merged cells is kept in its left top cell. The other cells of a merged cell are without a value.
This is why we always compare the merged cells with their "left" neighbour;
These are the OnEnd and OnStart, facilitating the operation.
Public Sub OnEnd()
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.AskToUpdateLinks = True
Application.DisplayAlerts = True
Application.Calculation = xlAutomatic
ThisWorkbook.Date1904 = False
Application.StatusBar = False
End Sub
Public Sub OnStart()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.AskToUpdateLinks = False
Application.DisplayAlerts = False
Application.Calculation = xlAutomatic
ThisWorkbook.Date1904 = False
ActiveWindow.View = xlNormalView
End Sub
Only one merge per group
EDITED to fix - thanks Vityata for the heads-up
Sub MergeEm()
Dim rw As Range, i As Long, s As Long, v
Range("C21:J33").Copy Range("C5:J17") 'for testing purposes: replace previous run
Application.ScreenUpdating = False
For Each rw In Range("C5:J17").Rows 'or wherever
i = 1
s = 1
Do While i < (rw.Cells.Count)
v = rw.Cells(i).Value
'check for a run of same values
Do While Len(v) > 0 And v = rw.Cells(i + s).Value
s = s + 1
If i + s > rw.Cells.Count Then Exit Do
Loop
'if s>1 then had a run: merge those ells
If s > 1 Then
Application.DisplayAlerts = False
rw.Cells(i).Resize(1, s).Merge
rw.Cells(i).HorizontalAlignment = xlCenter
Application.DisplayAlerts = True
i = i + s 'skip over the merged range
s = 1 'reset s
Else
i = i + 1
End If
Loop
Next rw
End Sub
I'm pretty sure what bloats your processing time is the goto causing you to loop through everything yet again every time after every merge
Edit to take column A into account and prevent first column cells to merge with cells outside of myRange:
Sub MergeSimilarCells()
Dim i As Long
Dim myCol As String
Set myRange = Range("K1:L30")
myCol = Left(myRange.Address(True, False), InStr(myRange.Offset(0, 1).Address(True, False), "$") - 1)
If Not Intersect(myRange, Range(myCol & ":" & myCol)).Address = myRange.Address Then
Set myRange = Range(Replace(myRange.Address, Left(myRange.Address(True, False), _
InStr(myRange.Address(True, False), "$")), Left(myRange.Offset(0, 1).Address(True, False), _
InStr(myRange.Offset(0, 1).Address(True, False), "$"))))
For i = myRange.Cells.Count To 1 Step -1
If myRange.Item(i).Value = myRange.Item(i).Offset(0, -1).Value And Not IsEmpty(myRange.Item(i)) Then
Range(myRange.Item(i), myRange.Item(i).Offset(0, -1)).Merge
myRange.Item(i).VerticalAlignment = xlCenter
myRange.Item(i).HorizontalAlignment = xlCenter
End If
Next
End If
End Sub
To clarify why myRange has to start in column B: Offset(0, -1) of any cell in column A will cause an error since there is no column to the left of A.

Avoid dependant event trigger each other

I have a Worksheet_change in which two events are checked (edits on cells of column C and edits on cells of column D). The problem is that an edit on column C's cells modify the value of column D's cells (and viceversa), so the Worksheet_change is triggered repeatedly and excel eventually crashes.
How can I avoid the problem but maintaining my functionality?
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rgFound As Range
Dim defVal As Range
Dim currParam As Range
Dim currParamDict As Range
Set targ = Intersect(Target, Range("A:A"))
If Not targ Is Nothing Then
With Worksheets("FT_CASE_xx")
For Each defVal In .Range("A2", .Range("A" & Rows.Count).End(xlUp)).Offset(, 1)
Set currParam = defVal.Offset(, -1)
Dim xlFirstChar As String
xlFirstChar = Left$(currParam, 1)
If xlFirstChar = "B" Then
Set rgFound = Worksheets("DEF_BOOLEAN").Range("A:A").Find(currParam.value)
defVal.Offset(, 1).Interior.Color = RGB(230, 230, 230)
defVal.Offset(, 1).Locked = True
defVal.Offset(, 2).Select
With Selection.Validation
.Delete
.Add Type:=xlValidateList, AlertStyle:=xlValidAlertStop, Operator:=xlBetween, Formula1:="TRUE,FALSE"
.IgnoreBlank = True
.InCellDropdown = True
.InputTitle = ""
.ErrorTitle = ""
.InputMessage = ""
.ErrorMessage = ""
.ShowInput = True
.ShowError = True
End With
Else
Set rgFound = Worksheets("DEF_FLOAT").Range("A:A").Find(currParam.value)
defVal.Offset(, 1).Interior.ColorIndex = 0
defVal.Offset(, 1).Locked = False
defVal.Offset(, 2).Locked = False
defVal.Offset(, 1).NumberFormat = "0.000"
defVal.Offset(, 2).NumberFormat = "0.000"
defVal.Offset(, 3).NumberFormat = "0.000"
End If
If rgFound Is Nothing Then
Debug.Print "Name was not found."
Else
If xlFirstChar = "B" Then
Set currParamDict = rgFound.Offset(, 3)
Else
Set currParamDict = rgFound.Offset(, 5)
End If
defVal.value = currParamDict.value
End If
Next defVal
End With
Else
Set targ = Intersect(Target, Range("C:C"))
If Not targ Is Nothing Then
Dim coeffVal As Range
Dim currVal As Range
Dim RequestedVal As Range
With Worksheets("FT_CASE_xx")
For Each coeffVal In .Range("C2", .Range("C" & Rows.Count).End(xlUp))
Set currVal = coeffVal.Offset(, -1)
Set RequestedVal = coeffVal.Offset(, 1)
Set ParamName = coeffVal.Offset(, -2)
Dim xlFirstChar2 As String
xlFirstChar2 = Left$(ParamName, 1)
If ((xlFirstChar2 = "F") And (IsEmpty(coeffVal.value) = False)) Then
RequestedVal.value = coeffVal.value * currVal.value
End If
Next coeffVal
End With
Else
Set targ = Intersect(Target, Range("D:D"))
If Not targ Is Nothing Then
Dim coeffsVal As Range
Dim val As Range
Dim reqVal As Range
Dim Parameter As Range
With Worksheets("FT_CASE_xx")
For Each reqVal In .Range("D2", .Range("D" & Rows.Count).End(xlUp))
Set coeffsVal = reqVal.Offset(, -1)
Set val = reqVal.Offset(, -2)
Set Parameter = reqVal.Offset(, -3)
Dim xlFirstChar3 As String
xlFirstChar3 = Left$(Parameter, 1)
If ((xlFirstChar3 = "F") And (IsEmpty(reqVal.value) = False)) Then
If val.value = 0 Then
coeffsVal.value = reqVal.value
Else
coeffsVal.value = reqVal.value / val.value
End If
End If
Next reqVal
End With
Else
Exit Sub
End If
End If
End If
End Sub
Maybe a different management of target intersection? How?
My favoured method (which can also be useful in other situations) is to create a variable at global or module level (as required) then check this on each run of the code
Private disableEvents as Boolean
Private Sub Worksheet_Change(ByVal Target As Range)
If disableEvents Then Exit Sub
disableEvents=True
<code here>
disableEvents=False
End sub

Simplify OR and AND on VBA

How do I simplify this function, to not be repetitive:
If [F11] = "" Or [F12] = "" Or [F13] = "" Or [F14] = "" Or [F15] = "" Or [F16] = "" Or [F17] = "" Or [F18] = "" Or [F19] = "" Or [F20] = "" Or [F21] = "" Then [...]
You can check if any blank cell exist in your range.
Option Explicit
Sub MyAnswer()
Dim rng As Range
Set rng = ActiveSheet.Range("F10:F100")
If rng.SpecialCells(xlCellTypeBlanks).Cells.Count = 0 Then
' Stuff you need
EndIf
End Sub
You can loop the range and break if any cell is ""
Option Explicit
Sub TestBlanks
Dim rngData As Range, rngCell As Range, blnRangeHasBlanks As Boolean
' assume no blanks
blnRangeHasBlanks = False
' iterate range and break on any blank
Set rngData = ThisWorkbook.Worksheets("Sheet1").Range("F11:F21")
For Each rngCell In rngData
If rngCell.Value = "" Then
blnRangeHasBlanks = True
Exit For
End If
Next For
If blnRangeHasBlanks Then
' ... do what you need
End If
End Sub

Color Excel rows if certain conditions apply using VBA

I have tried to make a macro to color excel rows if certain conditions apply, however, when I run it I get a syntax error in this line:
If (Not item1 (Cells(matchline, 1)) Then GoTo continue
Also, I'd like for a certain range to be colored, not the entire row. I have this from another macro, but don't know how to apply it correctly in ColorRows:
Range(Cells(Rng.row, "A"), Cells(Rng.row, "M")).Interior.Color = xlNone
Current code:
Option Explicit
Sub ColorRows()
Dim matchline As Integer, lastmatchline As Integer, lastbinline As Integer
Dim item1 As String, line As Integer, endline As Integer
'For line = 3 To endline
For matchline = 6 To lastmatchline
item1 = Cells(matchline, 1).Value
If (Not item1 (Cells(matchline, 1)) Then GoTo continue
If Not item1(Cells(matchline, 1)) Then GoTo continue
If (item1 = "Unexpected Status") Then _
Cells(matchline, 1).EntireRow.Font.Interior.Color = 13434828
If (item1 = "At Risk") Then _
Cells(matchlineline, 1).EntireRow.Font.Interior.Color = 8420607
If (item1 = "Requirements Definition") Then _
Cells(matchlineline, 1).EntireRow.Font.Interior.Color = 10092543
continue:
Next line
End Sub
Try something like:
Dim ws As Worksheet
Dim rows As Long, i As Long
Dim rngSearch As Range, rngColor As Range
Application.ScreenUpdating = False
Application.EnableEvents = False
Set ws = ActiveSheet
rows = ws.UsedRange.rows.Count
For i = 1 To rows
Set rngSearch = ws.Cells(i, 1)
Set rngColor = ws.Range("A" & i, "M" & i)
If rngSearch = "Unexpected Status" Then
rngColor.Interior.Color = 13434828
End If
If rngSearch = "At Risk" Then
rngColor.Interior.Color = 8420607
End If
If rngSearch = "Requirements Definition" Then
rngColor.Interior.Color = 10092543
End If
Next i
Application.ScreenUpdating = True
Application.EnableEvents = True

Combining IF else with LEFT to hide columns

I'm trying to write some code to Hide columns if the first 3 characters of cells in a range equal the contents of another. I have the code for hiding columns if cells in a range are blank as this;-
Private Sub Worksheet_Change(ByVal Target As Range)
Dim r As Range, cell As Range
On Error GoTo ErrHandler
Set r = Me.Range("C8:R8")
Application.ScreenUpdating = False
Application.EnableEvents = False
For Each cell In r
If cell.Value = "" Then
cell.EntireColumn.Hidden = True
Else
cell.EntireColumn.Hidden = False
End If
Next
ErrHandler:
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
And the code for identifiying the first 3 charcters of a cell;-
Dim LResult As String
LResult = Left ("Alphabet",3)
But how do I combine the two, referencing a specific cell rather than "Alphabet"?
Cant get this to work - any suggestions?
Private Sub Worksheet_Change(ByVal Target As Range)
Dim r As Range, cell As Range
On Error GoTo ErrHandler
Set r = Me.Range("B7:CG7")
Application.ScreenUpdating = False
Application.EnableEvents = False
Row = 1
col = 1
For Each cell In r
If cell.Value = "" And Left(cell.Value, 3) = cell(Row, col).Value Then
cell.EntireColumn.Hidden = True
Else
cell.EntireColumn.Hidden = False
End If
Next
ErrHandler:
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Cheers
You have almost the working code. You are comparing cell.Value to an empty string - now just apply Left to it
LResult = Left (cell.Value,3)
Edit:
row = 20
col = 30
For Each cell In r
If cell.Value = "" and Left (cell.Value,3) = Cell(row, col).Value Then
cell.EntireColumn.Hidden = True
Else
cell.EntireColumn.Hidden = False
End If
where you want data from cell at row and col (I used 20, 30 as the example)

Resources