How to automatically resize expanded rows? - vba

So my problem is that I have a sheet in which a multitude of grouped rows exist. The rows are grouped in 2 levels. To put this in perspective, I have a group which covers the rows in A1:A55. Inside this first level group I have multiple second level groups covering smaller sections (e.g. rows in A2:A5, rows in A7:A10 and so on.). Because of Excel automatically adding groups together if they are adjacent to each other, I have added a blank row in between each 2nd level group of rows(A6, A11, etc.). I then proceeded to change the height of these blank rows to 0,00. This hid the + and - signs on the left hand bar for collapsing/expanding, which wasn't a problem as the collapsing and expanding is being handled via buttons on the sheet.
However, when all the grouped rows, or just the 2nd level grouped rows, are being expanded (either manually or via a macro), the row height of all the blank rows jumps back to a size at which Excel can display the + and - signs in the left hand bar again. This shows the blank rows which I want to prevent.
I know I probably can't prevent the resizing of the rows so it displays the + and - signs, however I was thinking about immediately resizing the blank rows to a height of 0.00. This is being built in the macro that is called via the buttons, but the concern is when a user expands the rows manually. There is no event for collapsing and or expanding for me to use in an event handler. Is there any way for me to have an automatic response on a manual expand action by the user?
I have provided a example of the code used below.
Sub Select1Year_Click()
Dim ws1 As Worksheet
Set ws1 = Worksheets("Overview")
Dim ws2 As Worksheet
Set ws2 = Worksheets("Selection Tab")
Dim ROffset As Integer
ROffset = ((ws2.Range("B33").Value - 1) * 4) 'User defined starting Year
'value
On Error Resume Next
With ws1
.Range("AJ2").Rows.ShowDetail = False '2018
.Range("AJ7").Rows.ShowDetail = False '2019
.Range("AJ12").Rows.ShowDetail = False '2020
.Range("AJ17").Rows.ShowDetail = False '2021
.Range("AJ22").Rows.ShowDetail = False '2022
.Range("AJ27").Rows.ShowDetail = False '2023
.Range("AJ32").Rows.ShowDetail = False '2024
.Range("AJ37").Rows.ShowDetail = False '2025
.Range("AJ42").Rows.ShowDetail = False '2026
.Range("AJ47").Rows.ShowDetail = False '2027
.Range("AJ52").Rows.ShowDetail = False '2028
End With
If ws2.Range("B31").Value = 1 Then 'User selected 1 year to be shown in
'expanded view
ws1.Range("AJ2").Offset(0, ROffset).Rows.ShowDetail = True
End If
End Sub
'------------------------------------------------------------------------
Sub Select10Year_Click()
Dim ws1 As Worksheet
Set ws1 = Worksheets("Overview")
Dim ws2 As Worksheet
Set ws2 = Worksheets("Selection Tab")
Dim i As Integer
Dim ROffset As Integer
ROffset = ((ws2.Range("B33").Value - 1) * 4) 'User defined starting Year
'value
If ws2.Range("B31").Value = 3 Then 'User selected all years to be expanded
On Error Resume Next
ws1.Shapes("Select10Year").ControlFormat.Value = True
With ws1
.Range("AJ2").Rows.ShowDetail = True '2018
.Range("AJ7").Rows.ShowDetail = True '2019
.Range("AJ12").Rows.ShowDetail = True '2020
.Range("AJ27").Rows.ShowDetail = True '2021
.Range("AJ22").Rows.ShowDetail = True '2022
.Range("AJ27").Rows.ShowDetail = True '2023
.Range("AJ32").Rows.ShowDetail = True '2024
.Range("AJ37").Rows.ShowDetail = True '2025
.Range("AJ42").Rows.ShowDetail = True '2026
.Range("AJ47").Rows.ShowDetail = True '2027
.Range("AJ52").Rows.ShowDetail = True '2028
End With
If ROffset > 0 Then 'User has selected a different starting year then
'2018, so collapse are years before selected
'starting year
For i = 0 To i = ROffset Step 1
ws1.Range("AJ2").Offset(0, ROffset).Rows.ShowDetail = False
Next i
End If
End If
End Sub
Any help would be greatly appreciated.

You can have your macro being launched as a result of a Worksheet_Change() event.

Related

VBA Simplify code by modifying For i Next i

I am in the process of simplifying a series of statements which are unnecessarily complex. I wish to simplify the below example using a For i procedure, but I am unsure how to increase the scope of my statement to affect the visibility of more objects on the sheet (this may be an easy solve that I am missing somehow, tunnel vision may be in effect today).
Example:
Sheet1 contains 135 chart objects, which are labeled in the following pattern:
A1Z
A2Z
A3Z
A4Z
A5Z
A6Z
A7Z
A8Z
A9Z
B1Z
B2Z
B3Z
B4Z
B5Z
B6Z
B7Z
B8Z
B9Z
And so on, through to the final object on the sheet, "O9Z".
Currently a CommandButton_Click event is assigned button on the sheet that calls these (ugly) procedures, which are written as follows:
If Sheet2.Range("D12").Value = "A1Z" Then
Sheets("Charts").ChartObjects("A1Z").Visible = True
Sheets("Charts").ChartObjects("A2Z").Visible = False
Sheets("Charts").ChartObjects("A3Z").Visible = False
Sheets("Charts").ChartObjects("A4Z").Visible = False
Sheets("Charts").ChartObjects("A5Z").Visible = False
Sheets("Charts").ChartObjects("A6Z").Visible = False
Sheets("Charts").ChartObjects("A7Z").Visible = False
Sheets("Charts").ChartObjects("A8Z").Visible = False
Sheets("Charts").ChartObjects("A9Z").Visible = False
Sheets("Charts").ChartObjects("B1Z").Visible = False
Sheets("Charts").ChartObjects("B2Z").Visible = False
Sheets("Charts").ChartObjects("B3Z").Visible = False
Sheets("Charts").ChartObjects("B4Z").Visible = False
Sheets("Charts").ChartObjects("B5Z").Visible = False
Sheets("Charts").ChartObjects("B6Z").Visible = False
Sheets("Charts").ChartObjects("B7Z").Visible = False
Sheets("Charts").ChartObjects("B8Z").Visible = False
Sheets("Charts").ChartObjects("B9Z").Visible = False
I am able to simplify this bloated procedure somewhat using a For i statement:
If Sheet2.Range("D12").Value = "A1Z" Then
Dim i As Integer
For i = 2 To 9
Sheets("Charts").ChartObjects("A" & i & "Z").Visible = False
Sheets("Charts").ChartObjects("A1Z").Visible = True
Next i
One problem with my procedure however is that it will only affect the visibility of objects A2Z through A9Z without affecting objects B1Z-O9Z.
I believe it may be possible to add a second variable in addition to i that loops through each letter in a range "A", "B", "C", "D" and so on to letter "O" and adjust the For i statement to account for it, so that every object on the worksheet that does not match the value in quotes in the If statement (in this example, "A1Z") is hidden.
I am unsure of which method to employ to account for that range of letters however.
Try looping through all the chart objects.
Dim cht As ChartObject
For Each cht In Sheets("Chart").ChartObjects
cht.Visible = cht.Name = "A1Z"
Next cht
If you want the chart that is visible to be dynamic then:
Dim cht As ChartObject
For Each cht In Sheets("Chart").ChartObjects
cht.Visible = cht.Name = Sheet2.Range("D12").Value
Next cht
You could try something like this using a for each loop:
Dim chartObj As ChartObject, strTest As String
strTest = Sheet2.Range("D12").Value
For Each chartObj In Sheets("Charts").ChartObjects
If chartObj.Name = strTest Then
chartObj.Visible = True
Else
chartObj.Visible = False
End If
Next chartObj
A for each loop enables you to iterate through each object in a collection, for example you could do it for each worksheet in sheets

Excel VBA using Combobox with Index Match

I have an Excel VBA UserForm Combobox for scanning asset tags to compare against a site baseline held in Sheet1. There can be upto 50,000+ assets. The named ranges are all correct.
I want the loop to fill the "Found" Asset attribute Textboxes for Type, Serial, MakeModel, Location & PrinterHost.
The code is below without the additional index match lookups for extra asset attributes as the process will be the same. Help appreciated as I'm not sure where I'm going wrong. Thanks in advance.
Private Sub ComboScanTag_Change()
Dim x As Integer
Dim AssetCount As Long
Dim BASELINE As Range
Dim AssetID As Range
Dim FoundType As Variant
Dim FoundSerial As Variant
Dim FoundMakeModel As Variant
Dim FoundLocation As Variant
Dim FoundPrinterHostName As Variant
If Me.ComboScanTag.Value = "" Then 'ScanTag has no value
MsgBox "Asset not Found - Re-Scan or enter New Asset details"
Me.ComboScanTag.SetFocus
End If
If Me.ComboScanTag.Value <> "" Then 'ScanTag has a value
Application.ScreenUpdating = False 'Turn off screen updating to speed app
For x = 1 To AssetCount 'Number of loop iterations from Baseline Assets Count D1 cell
FoundType = Application.Index("BASELINE", Application.Match(Me.ComboScanTag.Value, "AssetID", False), 3)
If Not IsError(FoundType) = False Then 'if error value in lookup return 0
Me.txtFoundType.Value = FoundType 'Fill textbox FoundType with lookup value from baseline
Else
On Error GoTo 0 'reset error handler
FoundSerial = Application.Index("BASELINE", Application.Match(Me.ComboScanTag.Value, "AssetID", False), 11)
If Not IsError(FoundSerial) = False Then
Me.txtFoundSerial.Value = FoundSerial
End If
Next x
End If
Application.ScreenUpdating = True
End Sub
AssetCount is not initialized. You need to initialize it before you use it like AssetCount = 10.
BASELINE and AssetID are not set as well.
If BASELINE and AssetID are named ranges, you cannot use it the way you do in Application.Index or Application.Match.
You need to pass it as object and not as string like this:
Set BASELINE = ThisWorkbook.Names("BASELINE").RefersToRange
Set AssetID = ThisWorkbook.Names("AssetID").RefersToRange
Then you can use it like this in Application.Index and Match:
With Application
FoundType = .Index(BASELINE, .Match(Me.ComboScanTag.Value, AssetID, False), 3)
End With

Show certain rows but keep others hidden

I am working with an excel workbook and on one of the worksheets are rows that hide or show depending on options selected in another worksheet. The structure looks something like this
A
1
2
3
4
B
1
2
3
4
C
1
2
3
4
Where they have the option to hide all of A and B, A and C, A, B, or C. The user has the option to hide A and B or C (they must select between B or C).They also have the option to hide the individual rows under each letter. Rows 1, 2, and 3. If the option to hide 2 is checked, all "2" rows under each letter is hidden. If they unchecked this option, all 2 rows appear once more. The problem is that the "2" row of an already hidden letter will display.
I have run into a mental block, but this is what I've done. Psuedocode for readability because right now my code is messy and I hate the way vba looks. This is a logic problem more than a syntax problem anyway.
Property hiddenA As Bool get let
Property hiddenB As Bool get let
Property hiddenC As Bool get let
OptionButton1.Click()
hiddenA = true
Hide A row and all rows associated with it
OptionButton2.Click()
HiddenA = false
Show A row and all rows associated with it
OptionButton3.Click()
HiddenB = false
HiddenC = true
Show B row and all rows associated with it
Hide C row and all rows associated with it
OptionButton4.Click()
HiddenB = true
HiddenC = false
Hide B row and all rows associated with it
Show A row and all rows associated with it
CheckBox1.Click()
if CheckBox1.value = false Then
Hide all "1" rows
Else
Show all "1" rows, but keep the "1"s under already hidden letters, hidden.
This is the problem.
And so on. There are checkboxes for showing/hidden all 2, 3, and 4 rows as well.
Rough outline - UNTESTED CODE... something like this should unhide everything, check each checkbox for status, add checked boxes to range, and hide that entire range at end.
'CheckBox1 is Row 1 in group
'CheckBox2 is Row 2 in group
'CheckBox3 is Row 3 in group
'CheckBox4 is Row 4 in group
'CheckBox5 is Row 5 in group
'CheckBox6 is Group A
'CheckBox7 is Group B
'CheckBox8 is Group C
'CheckBox9 is Group D
'CheckBox10 is Group E
Sub CheckBoxClick() 'Assign this to all checkboxes
Application.ScreenUpdating = False 'Turn off screen updating
ActiveSheet.Cells.EntireRow.Hidden = False 'Unhide all
Dim RngCnt As Range
Dim LastRow As Long
Dim CurRow As Long
Dim ChkBx As OLEObject
LastRow = Range("A" & Rows.Count).End(xlUp).Row
For Each ChkBx In ActiveSheet.OLEObjects
If TypeName(ChkBx.Object) = "CheckBox" Then
Select Case ChkBx.Name
Case "CheckBox1"
If ChkBx.Value = True Then
RngCnt = Union(RngCnt, Range(Rows this chk box effects))
End If
Case "CheckBox2"
If ChkBx.Value = True Then
RngCnt = Union(RngCnt, Range(Rows this chk box effects))
End If
Case ETC, ETC, ETC to "CheckBox10"
...
End If
Next ChkBx
RngCnt.EntireRow.Hidden = True
Application.ScreenUpdating = True
End Sub

Hiding multiple rows based on cell values

I have an algorithm that works fine to hide all rows where, in a specified named range, a given row has the value 0. It's straightforward enough:
Public Sub MasquerLignesAZeroRapport(rap As Worksheet)
Dim cell As Range
rap.Rows.Hidden = False
For Each cell In rap.Range("Ra_LignesAZero")
If Round(cell.Value, 0) = 0 Then
cell.EntireRow.Hidden = True
End If
Next cell
End Sub
This, however, takes a bit of time even when calculation and screen updating are turned off and I have tried different other methods without success (using a filter and hiding all visible rows but removing the filter unhides the rows, the same goes for setting the row height to 0).
Is there a faster alternative ? I can live with that slow algorithm but it would be a welcome improvement as this macro may be run against 1-6 reports in a single run.
Here are a few optimizations:
Public Sub MasquerLignesAZeroRapport(rap As Worksheet)
'Optimization #1: as you pointed out, turning off calculations
' and screen updating makes a difference.
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
rap.Rows.Hidden = False
'Optimization #2: instead of loading each cell as a range,
' with all the associated properties, load JUST the values
' into a 2 dimensional array.
Dim values() As Variant
values = rap.Range("Ra_LignesAZero")
For r = 1 To UBound(values, 1)
For c = 1 To UBound(values, 2)
If Round(values(r,c), 0) = 0 Then
rap.Rows(r).Hidden = True
'Optimization #3: if we have already determined
' that the row should be hidden, no need to keep
' looking at cells in the row - might as well break out of the For:
Exit For
End If
Next
Next
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub

VBA Deselect all and then Select Values based on list in Slicer

I am trying to deselect all pre-set values in a slicer and then select values based on a list that will be updated on a daily basis. I managed to write the following code but it is running very very slow since my pre-set list was long. Is there a way to make the following run faster?
Sub cmdTop10PrioritySlicer()
Dim i As Integer
Dim MemberID As String
Application.ScreenUpdating = False
ThisWorkbook.SlicerCaches("Slicer1").ClearManualFilter
'Deselect all prior set values
For Each slc In ThisWorkbook.SlicerCaches("Slicer1").SlicerItems
slc.Selected = False
Next slc
'Select values in Slicer based on Top 10 Priority List
For i = 1 To 10
ID = ThisWorkbook.Worksheets("Summary").Range("A" & 57 + i).Value
ThisWorkbook.SlicerCaches("Slicer1").SlicerItems(ID).Selected = True
Next i
Application.ScreenUpdating = True
End Sub