Excel VBA read pivot table and display msgbox - vba

I have a pivot table and the following VBA which displays a msgbox for the first row field, but I need it to go through all row fields displaying a message box for each one, can someone point me in the right direction, I cant seem to work out how to do it
Sub Piv()
Dim PvTable As PivotTable
Dim PvField As PivotField
Dim PvItem As PivotItem
Set PvTable = ActiveSheet.PivotTables("RawDataTable")
Set PvField = PvTable.RowFields(1)
With ws
For Each PvItem In PvField.PivotItems
MsgBox PvItem
Next
End With
End Sub
I can also get it to give me all the field headers, but not the data
Sub Piv()
Dim PvTable As PivotTable
Dim PvField As PivotField
Dim PvItem As PivotItem
Set PvTable = ActiveSheet.PivotTables("RawDataTable")
With ws
For Each PvField In PvTable.PivotFields
MsgBox PvField
Next
End With
End Sub

This is a fairly brute-force approach, and hopefully someone will come up with something more elegant, but we can read the details of the row fields into an array of arrays, and then run through this array in reverse index order:
Option Explicit
Sub Piv()
Dim PvTable As PivotTable
Dim PvField As PivotField
Dim PvItem As PivotItem
Dim dataArray() As Variant
Dim dummyArray() As Variant
Dim i As Long
Dim j As Long
Set PvTable = ActiveSheet.PivotTables("RawDataTable")
ReDim dataArray(1 To PvTable.RowFields.Count)
ReDim dummyArray(1 To PvTable.RowFields(1).PivotItems.Count)
For i = 1 To PvTable.RowFields.Count
dataArray(i) = dummyArray
For j = 1 To PvTable.RowFields(i).PivotItems.Count
dataArray(i)(j) = PvTable.RowFields(i).PivotItems(j)
Next j
Next i
For i = 1 To UBound(dataArray(1))
For j = 1 To UBound(dataArray)
MsgBox dataArray(j)(i)
Next j
Next i
End Sub

Related

Unique list from a matrix to a single column

I needed to collect a unique list of text from a matrix, ("J19:BU500" in my case which contains duplicates) and paste it in a column (column DZ in my case) in the same sheet.
I need to loop this for multiple sheets in the same workbook. I'm new to VBA and got this code from internet and customized a bit to my requirement. But I have two problems with the code:
When the matrix is empty in say sheet 5, the code runs fine upto sheet 4 and throws a runtime error at sheet5 and stops without looping further to next sheets.
Also, I actually wanted the unique list to start at Cell "DZ10". If I do that, the number of unique list reduces by 10. For say there are 25 uniques, only 15 gets pasted starting from cell "DZ10" whereas all 25 gets pasted from cell "DZ1".
Code:
Public Function CollectUniques(rng As Range) As Collection
Dim varArray As Variant, var As Variant
Dim col As Collection
If rng Is Nothing Or WorksheetFunction.CountA(rng) = 0 Then
Set CollectUniques = col
Exit Function
End If
If rng.Count = 1 Then
Set col = New Collection
col.Add Item:=CStr(rng.Value), Key:=CStr(rng.Value)
Else
varArray = rng.Value
Set col = New Collection
On Error Resume Next
For Each var In varArray
If CStr(var) <> vbNullString Then
col.Add Item:=CStr(var), Key:=CStr(var)
End If
Next var
On Error GoTo 0
End If
Set CollectUniques = col
End Function
Public Sub WriteUniquesToNewSheet()
Dim wksUniques As Worksheet
Dim rngUniques As Range, rngTarget As Range
Dim strPrompt As String
Dim varUniques As Variant
Dim lngIdx As Long
Dim colUniques As Collection
Dim WS_Count As Integer
Dim I As Integer
Set colUniques = New Collection
WS_Count = ActiveWorkbook.Worksheets.Count
For I = 3 To WS_Count
Sheets(I).Activate
Set rngTarget = Range("J19:BU500")
On Error GoTo 0
If rngTarget Is Nothing Then Exit Sub '<~ in case the user clicks Cancel
Set colUniques = CollectUniques(rngTarget)
ReDim varUniques(colUniques.Count, 1)
For lngIdx = 1 To colUniques.Count
varUniques(lngIdx - 1, 0) = CStr(colUniques(lngIdx))
Next lngIdx
Set rngUniques = Range("DZ1:DZ" & colUniques.Count)
rngUniques = varUniques
Next I
MsgBox "Finished!"
End Sub
Any help is highly appreciated. Thankyou
You need to select the correct amount of cells to fill in all data from an array. Like Range("DZ10").Resize(RowSize:=colUniques.Count)
That error probably means that colUniques is nothing and therefore has no .Count. So test if it is Nothing before you use it.
You will end up with something like below:
Public Sub WriteUniquesToNewSheet()
Dim wksUniques As Worksheet
Dim rngUniques As Range, rngTarget As Range
Dim strPrompt As String
Dim varUniques As Variant
Dim lngIdx As Long
Dim colUniques As Collection
Dim WS_Count As Integer
Dim I As Integer
Set colUniques = New Collection
WS_Count = ActiveWorkbook.Worksheets.Count
For I = 3 To WS_Count
Sheets(I).Activate
Set rngTarget = Range("J19:BU500")
'On Error GoTo 0 'this is pretty useless without On Error Resume Next
If rngTarget Is Nothing Then Exit Sub 'this is never nothing if you hardcode the range 2 lines above (therefore this test is useless)
Set colUniques = CollectUniques(rngTarget)
If Not colUniques Is Nothing Then
ReDim varUniques(colUniques.Count, 1)
For lngIdx = 1 To colUniques.Count
varUniques(lngIdx - 1, 0) = CStr(colUniques(lngIdx))
Next lngIdx
Set rngUniques = Range("DZ10").Resize(RowSize:=colUniques.Count)
rngUniques = varUniques
End If
Next I
MsgBox "Finished!"
End Sub

Subscript out of range - runtime error 9

can you please advise why the below code does not select the visible sheets, but ends in a runtime error. This is driving me crazy. Thanks for any help.
Sub SelectSheets1()
Dim mySheet As Object
Dim mysheetarray As String
For Each mySheet In Sheets
With mySheet
If .Visible = True And mysheetarray = "" Then
mysheetarray = "Array(""" & mySheet.Name
ElseIf .Visible = True Then
mysheetarray = mysheetarray & """, """ & mySheet.Name
Else
End If
End With
Next mySheet
mysheetarray = mysheetarray & """)"
Sheets(mysheetarray).Select
End Sub
Long story short - you are giving a string (mysheetarray) when it is expecting array. VBA likes to get what it expects.
Long story long - this is the way to select all visible sheets:
Option Explicit
Sub SelectAllVisibleSheets()
Dim varArray() As Variant
Dim lngCounter As Long
For lngCounter = 1 To Sheets.Count
If Sheets(lngCounter).Visible Then
ReDim Preserve varArray(lngCounter - 1)
varArray(lngCounter - 1) = lngCounter
End If
Next lngCounter
Sheets(varArray).Select
End Sub
You should define Dim mySheet As Object as Worksheet.
Also, you can use an array of Sheet.Names that are visible.
Code
Sub SelectSheets1()
Dim mySheet As Worksheet
Dim mysheetarray() As String
Dim i As Long
ReDim mysheetarray(Sheets.Count) '< init array to all existing worksheets, will optimize later
i = 0
For Each mySheet In Sheets
If mySheet.Visible = xlSheetVisible Then
mysheetarray(i) = mySheet.Name
i = i + 1
End If
Next mySheet
ReDim Preserve mysheetarray(0 To i - 1) '<-- optimize array size
Sheets(mysheetarray).Select
End Sub
I have tried to explain the Sheets a little, HTH.
Note: Sheets property is defined on Workbook and on Application objects, both works and returns the Sheets-Collection.
Option Explicit
Sub SheetsDemo()
' All sheets
Dim allSheets As Sheets
Set allSheets = ActiveWorkbook.Sheets
' Filtered sheets by sheet name
Dim firstTwoSheets As Sheets
Set firstTwoSheets = allSheets.Item(Array("Sheet1", "Sheet2"))
' or simply: allSheets(Array("Sheet1", "Sheet2"))
' Array("Sheet1", "Sheet2") is function which returns Variant with strings
' So you simply need an array of sheet names which are visible
Dim visibleSheetNames As String
Dim sh As Variant ' Sheet class doesn't exist so we can use Object or Variant
For Each sh In allSheets
If sh.Visible Then _
visibleSheetNames = visibleSheetNames & sh.Name & ","
Next sh
If Strings.Len(visibleSheetNames) > 0 Then
' We have some visible sheets so filter them out
visibleSheetNames = Strings.Left(visibleSheetNames, Strings.Len(visibleSheetNames) - 1)
Dim visibleSheets As Sheets
Set visibleSheets = allSheets.Item(Strings.Split(visibleSheetNames, ","))
visibleSheets.Select
End If
End Sub

Color data points based on data label text - Excel VBA

I have the code below which colors bubble chart data points based on the data label text. I'm not sure why I am keeping an "Invalid paramter error"
Edited for more clarity.
The code loops through a spreadsheet where I have data label filter critieria stored(see image attached). It will copy a pre-made bubble graph and color it. variable f loops between variables a and c, and based on the values in between these two variables, the bubble chart will color if it matches. If not, it moves past it. After bubbles are colored, it moves on to the next variation of coloring.
Sub Slide31()
Dim rngx As Range
Dim rngy As Range
Dim rngz As Range
Dim ws3 As Worksheet
Dim ws As Worksheet
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim icnt As Long
Dim lastrow As Long
Dim k As Long
Dim icounter As Long
Dim a As Long
Dim c As Long
Dim b As Long
Dim d As Variant
Dim Chart As ChartObject
Dim PPapp As Object
Dim PPTDoc As PowerPoint.Presentation
Dim PPT As PowerPoint.Application
Dim PPpres As Object
Dim pptSlide As PowerPoint.Slide
Dim ppslide As Object
Dim e As Long
Dim f As Long
Dim filename As String
Dim filename2 As String
Dim x As Variant
Dim y As Variant
Dim z As Variant
Dim ch As Chart
Dim s As Series
Dim iPoint As Long
Dim nPoint As Long
Set ws = Worksheets("Reference")
Set ws1 = Worksheets("Bubbles")
Set ws2 = Worksheets("Slide 31")
Set ws3 = Worksheets("Bubble Reference")
ws2.Activate
'ws2.Range("h:h").NumberFormat = "0.00%"
lastrow = ws2.Cells(Rows.Count, "b").End(xlUp).Row
For icounter = 1 To lastrow
For icnt = 51 To 79
If ws2.Cells(icounter, 2) = ws.Cells(icnt, 3) Then
d = ws.Cells(icnt, 3)
a = icounter + 2
b = icounter + 2
c = icounter + 11
filename = ""
filename2 = ""
ws3.ChartObjects(1).Copy
ws2.Paste
Set ch = ActiveChart
Set s = ch.SeriesCollection(1)
For f = a To c
nPoint = s.Points.Count
For iPoint = 1 To nPoint
If ws2.Cells(f, 8) = s.Points(iPoint).DataLabel.Text Then
s.Points(iPoint).Format.Fill.ForeColor.RGB = RGB(192, 0, 0)
End If
Next iPoint
Next f
End If
Next icnt
Next icounter
Point object doesn't have an Interior property. (Edit: Yes, it actually does even though the Dox and the Intellisense do not seem to expose it).
(Point object reference | Excel Reference)
The specific error you're getting (1004, "Invalid parameter error") is akin to Index Out of Bounds, you're somehow trying to index the Points collection in an invalid way, though I'm not sure how this is possible. You can easily get this error if you try s.Points(0) or s.Points(s.Points.Count+1), for instance.
You could try this alternative approach:
Dim pt as Point
For Each pt in s.Points
If ws2.Cells(f, 8) = pt.DataLabel.Text Then
pt.Format.Fill.ForeColor.RGB = RGB(192, 0, 0)
End If
Next

object required within collection vba

seems that I am a bit rusty when it comes to vba programming. I have created a licence type (class/object) and wishing to add that to a collection type. I am trying to iterate over the collection but keep getting object required error 424. Code snippet below for advise. thanks in advance
Private Sub btnGenerate_Click()
Dim lic As licence
For Each lic In licenceCollection
Debug.Print lic.getClause
Next lic
End Sub
error produced on for each lic in licenceCollection
Private Sub cboHeading_Change()
Dim heading As String
Dim str As String
'Dim lic As Licence
Dim rngValue As Range
Dim ws As Worksheet
Dim last_row As Long
Dim arr()
Dim i As Long
'Dim lic As licence
heading = cboHeading.Value
Set licenceCollection = New collection
Select Case heading
Case "Future Sampling"
'str = "lorem ipsum"
'Utility.createCheckBoxes (str)
'grab data from Future Sampling ws
Set ws = Worksheets("Future_Sampling")
ws.Activate
last_row = Range("A2").End(xlDown).Row
Debug.Print last_row
ReDim arr(last_row - 2)
'add array to object type
For i = 0 To last_row - 2
arr(i) = Range("A" & i + 2)
'Debug.Print arr(i)
Next
Set licence = New licence
licence.setClause = arr
'Debug.Print lic.getDescription
'add licence to collection for later retrieval
licenceCollection.Add (arr)
Case Else
Debug.Print ("no heading")
End Select
'Set lic = Nothing
End Sub
Private Sub UserForm_Initialize()
Dim rngValue As Range
Dim ws As Worksheet
Set ws = Worksheets("Headings")
For Each rngValue In ws.Range("A2:A10")
Me.cboHeading.AddItem rngValue.Value
Next rngValue
'licenceForm.cboHeading.SetFocus
'create vertical scrollbar
With Me.resultFrame
.ScrollBars = fmScrollBarsVertical
End With
End Sub
Thanks guys, that fixed my issue.
Private Sub btnGenerate_Click()
Dim i As Long
Dim lic As licence
Dim temp As Variant
For Each lic In licenceCollection
temp = lic.getClause
Next lic
For i = LBound(temp) To UBound(temp) Step 1
Debug.Print temp(i)
Next
End Sub
Private Sub cboHeading_Change()
Dim heading As String
Dim str As String
'Dim lic As Licence
Dim rngValue As Range
Dim ws As Worksheet
Dim last_row As Long
Dim arr()
Dim i As Long
Dim lic As licence
heading = cboHeading.Value
Set licenceCollection = New collection
Select Case heading
Case "Future Sampling"
'str = "lorem ipsum "
'Utility.createCheckBoxes (str)
'grab data from Future Sampling ws
Set ws = Worksheets("Future_Sampling")
ws.Activate
last_row = Range("A2").End(xlDown).Row
Debug.Print last_row
ReDim arr(last_row - 2)
'add array to object type
For i = 0 To last_row - 2
arr(i) = Range("A" & i + 2)
'Debug.Print arr(i)
Next
Set lic = New licence
lic.setClause = arr
'Debug.Print lic.getDescription
'add licence to collection for later retrieval
licenceCollection.Add lic
Case Else
Debug.Print ("no heading")
End Select
'Set lic = Nothing
End Sub

Filter Pivot Tables with Checkbox

I'm working with the following code:
Option Explicit
Sub checkboxfilter()
Dim cb As CheckBox
Dim oWS As Worksheet
Dim oWB As Workbook
Dim oPvt As PivotTable
Dim oPvtField As PivotField
Dim oPvtFilter As PivotFilter
Set cb = oWS("Control").Controls("YTD Filter")
If cb.Value = True Then
For Each oWS In ThisWorkbook.Worksheets
For Each oPvt In oWS
With oPvtField
.CurrentPage.Name = "Yes"
End With
Next oPvt
Next oWS
End If
End Sub
the goal is to toggle each pivot table in the workbook by a yer-to-date filter via checkbox. The code hits a snag under set cb= as an object variable or with not set. What am I missing here to get this control working? I'm also avoiding the use of a slicer.
Thanks.
That kind of control has it's own event and you should use it. Therefore:
go to sheet where you have your checkbox
set Design mode on developer tab on
double click on you check box to ...
...see something like Private Sub CheckBox1_Click()
inside that sub call your subroutine:
Private Sub CheckBox1_Click()
call checkboxfilter
End Sub
I was able to revise based on adjusting the type of format the set cb = as a .Checkboxes and ensuring at each pivot fielt was accurately called as #KazimierzJawor pointed out. Additionally with this type the value needed to be a 0 or 1 rather than True or False. Corrected and final code below.
Private Sub checkboxfilter()
Dim cb As CheckBox
Dim oWS As Worksheet
Dim oWB As Workbook
Dim oPvt As PivotTable
Dim oPvtField As PivotField
Dim oPvtFilter As PivotFilter
Set cb = Sheets("Control").CheckBoxes("YTD Filter")
If cb.Value = 1 Then
For Each oWS In ThisWorkbook.Worksheets
For Each oPvt In oWS.PivotTables
With oPvt.PivotFields("YTD?")
.CurrentPage = "Yes"
End With
Next oPvt
Next oWS
Else
For Each oWS In ThisWorkbook.Worksheets
For Each oPvt In oWS.PivotTables
With oPvt.PivotFields("YTD?")
.CurrentPage = "(All)"
End With
Next oPvt
Next oWS
End If
End Sub
Sub PivotFilter()
Dim pvtF As PivotField
Dim pvtI As PivotItem
Dim StartDate As Date
Dim EndDate As Date
Dim pvtIVal As String
StartDate = DateValue("Jan 1, 2018")
EndDate = Application.WorksheetFunction.EoMonth(Date, 0)
ActiveSheet.PivotTables("PivotTable1").PivotFields("Create Month").ClearAllFilters
Set pvtF = ActiveSheet.PivotTables("PivotTable1").PivotFields("Create Month")
For Each pvtI In pvtF.PivotItems
If (pvtI <> "(blank)") Then
If DateValue(pvtI) >= StartDate And DateValue(pvtI) <= EndDate Then
pvtI.Visible = True
Else
pvtI.Visible = False
End If
Else
pvtI.Visible = False
End If
Next pvtI
End Sub