Referencing Pivot Table data using VBA - excel

I'm trying to list row labels of a pivot table but can't access them. They don't show up as row labels so I am not sure.
Example:
Row Labels Sum of Resources
---------- ----------------
item 1 1
item 2 4
item 3 2
I want to be able to access item1, item2, and item3.
The code I have so far is below and it does what I want except for access items 1 to 3:
Dim item As Variant
For Each item In ActiveSheet.PivotTables("Pivot1"). _
PivotFields("Sum of Resource").DataRange
Debug.Print item;
Next item

Refer directly to the cell
Further to our comments, this is not the best way to get a summary of data, but you seem determined, and without more information about what you have and what you're trying to do, I can only wonder why you don't just refer to the label cells "like any other cell".
Example:
In the example above, =D2 returns A, which is the first label of the PivotTable.
The GetPivotData setting & functions
Note that your formulas with the above examlpe might appear differently depending on your settings (in Options) for "Use GetPivotData functions for PivotTable references" (mine is unselected), but the end result is the same either way.
Click image to enlarge.
There is more information about GetPivotData in the documentation here.
The DataLabelRange property
There are several other ways of referring to [any] part of a PivotTable, some more reliable than others if the data is changing often. An alternative could be to use the DataLabelRange property.
More on the DataLabelRange property in the documentation here and examples here.

I was able to figure out how to do this. Not sure if anyone else has this issue but the code below will list all the row labels.
'Get Row labels
For Each item In ActiveSheet.PivotTables("Pivot-Table").RowRange
If Not count = 0 And Not count = ActiveSheet.PivotTables("Pivot-Table").RowRange.count - 1 Then
Debug.Print item
End If
count = count + 1
Next item
This code will print all row labels ignoraing the grand total and row label caption

Related

How to filter list displayed on Excel userform using VBA

First - I am NOT a programmer, so please be patient and know I may not use the correct terminology. :)
I am 95% done with a project using VBA in Excel Developer (first time ever!) to create a userform to populate a spreadsheet with organizational risks. At the bottom of the userform, I have a list that displays the risks already entered into the spreadsheet for reference, but I want to exclude "closed" risks from list view on the userform (but I want to retain them in the source spreadsheet).
Column J (column 10) contains the status, so I need something like Column J <> "Closed" (wildcards because there are different types of "closed") in the if/then statement, but I don't have the right syntax or maybe I need to define Column J so the filter knows where to look?? A friend suggested I may need to filter in a completely separate sub - but not sure how to accomplish that. Excerpt below.
'define number of columns (fields) in the database to display at the bottom of screen and identify column headers
.lstRiskDatabase.ColumnCount = 11
.lstRiskDatabase.ColumnHeads = True
'Assign column widths - note summary column is intentionally 0
.lstRiskDatabase.ColumnWidths = "35,55,75,175,150,160,0,55,55,100,100"
'assign control source for rows on the database table
If iRow > 1 Then
.lstRiskDatabase.RowSource = "RiskDatabase!A2:K" & iRow
Else
.lstRiskDatabase.RowSource = "RiskDatabase!A2:K2"
End If

Excel Listobject table ListRows.count vs range.rows.count

I am working with listobjects in Excel and I have the following problem:
I add data to a table everytime a code is run.
Previously I have to delete all the old data.
ThisWorkbook.Sheets("comm").ListObjects(1).DataBodyRange.Delete
What happend afterwards is that I get an error with:
myNrofRowsinCOMM = COMMtbl.DataBodyRange.Rows.Count
I had a look to this post at no avail. I still dont understand what is going on.
I tried the follwoing as well:
MsgBox "COMMtbl.Range.Rows.Count:" & COMMtbl.Range.Rows.Count
MsgBox "COMMtbl.ListRows.Count:" & COMMtbl.ListRows.Count
MsgBox "COMMtbl.databodyRange.Rows.Count:" & COMMtbl.DataBodyRange.Rows.Count
If COMMtbl.Range.Rows.Count = 1 Then
COMMtbl.ListRows.Add (1)
End If
If the table is empty (row headers and an empty first row) the first line gives 2. So the range has 2 rows which seems according to reality.
COMMtbl.Range.Rows.Count=2
the sencond one gives 0. Which I dont understand at all.
COMMtbl.ListRows.Count=0
And the third one gives an error
"Object variable or withblcok variable not set"
I am trying to add rows to the table and fill them with data, for that I add a row and populate it. I want to add a row at the end, therefore I need everytime to count the number of rows. ALL fine except for the firts one when I previously deleted the whole content of the table which looks like:
Any help is welcome
Thanks a lot.
I needed a refresher, so this might help you too:
.
.Range - Includes the Header, Insert Row and Totals Row (if visible)
.DataBodyRange
- Contains the data area, between the Header Row and the Insert Row
- If the ListObject doesn't have a DataBodyRange, this property returns Null
.ListRows
- Represents all the rows of data (doesn't include Header, Total, or Insert rows)
- To delete any items from this collection, do not use the Delete method of the item
Use the Delete method of the range of the item to delete the item
For example ListRows.Item(1).Range.Delete()
.
When you do DataBodyRange.Delete the table doesn’t have the DataBodyRange object anymore, so to confirm that there are no rows with data in the table, replace
myNrofRowsinCOMM = COMMtbl.DataBodyRange.Rows.Count
with
myNrofRowsinCOMM = COMMtbl.ListRows.Count
More details from MSDN - ListObject
If you don't have data in the ListObject.DataBodyRange Is Nothing, so you can't count rows. You can get the last row of the ListObject by using n = ListObject.Range.Rows.Count and then ListObject.ListRows(n).Range
I don't know how the data you have at hand looks like, but for the sake of the example, if all you had was one column and one row, you could add the data to the last row without worrying if the table is empty or not and then using the .Add method in your example.
Dim current_n_rows As Integer
With ThisWorkbook.Sheets("comm").ListObjects(1)
.DataBodyRange.Delete
current_n_rows = .ListRows.Count
.ListRows(current_n_rows).Range.Value = NewData
.ListRows.Add
End With
You can wrap this statement around the loops you would need to fill the table.
Hope it helps! Cheers!

Excel Bar/Column graph showing blank cells

Starting with screenshot:
http://i.imgur.com/Isj9MER.png
(I'm a new user, can't post images)
Working for a call center. We have a program that tracks our time spent in various phone states (so when we're on calls, out to lunch, etc) that can export data for a given team and date range as a CSV.
I'm working on automating this report. The way it works is that the team lead will pull the CSV, copy-paste into another tab, and then I've got a bunch of array formula If functions and Indirect references to pull all the data as shown. The data analysis and everything is working great.
My problem is the graph. Right now, I've got column B with an If function that either outputs the agent's email (which is how the system tracks it) or "" if all emails have been used. The rest of the columns have If(B2="","", [relevant formula]). That way, we can have all the team leads with various (and fluctuating) team sizes use the same report with a simple copy-paste.
My problem is the stupid bar chart. It pulls data from rows 2-32 (A2:A32). Our current largest team is 28, and I left room for new hires showing up soon. My problem happens when I use data from one of our smaller teams. As you can see, even though the blank rows are filled with "" in every cell, it's still displaying those rows. In the chart. This means that with the smallest team (shown), the chart is half wasted whitespace.
Is there a way to make the column chart only show rows that have actual data in them?
One thing I tried was putting an Indirect reference for Series Values. So I had a cell (AA1) with {=MAX(IF(B2:B31="","",ROW(B2:B31)))}. That outputs the row number of the last non-blank row. Then for the Series Values I put =Indirect("Report!A2:A"&AA1), but Excel gave me an error saying the function was not valid. I guess you can only have an actual range (and not a formula) in the data input for a chart.
Excel 2016, by the way.
I come up with three possible solutions for this problem.
Convert your data table to a pivot table and use a pivot chart (currently only available in Windows version, sorry if you are using a Mac).
Use a bit of VBA to hide the empty rows.
Use a bit of VBA to modify the data displayed on the chart.
Sample Data Setup
Although you provided a screen shot of your data, it is not simple to convert that into a test case to demonstrate the three solutions. Therefore, I threw together this very simple set up.
Column A contains a list of possible "users".
Cell D1 is a user entry to change the contents of column B and C.
Column B contains the actual "users". It is calculated with =IF(A2<=$D$1,A2,"")
Column C contains the data that goes with the "users". It is calculated with =IF(B2<>"",10,"")
A chart is added to the sheet.
Below is a screenshot of the sample setup, where all potential users are included. (Note: It is Sheet1)
Below is a screenshot of the sample setup, where only potential users A through E are included.
The white space in the second image is the problem we are trying to address.
SOLUTION 1: Make a pivot table of the data
Select all of the pertinent data, in this case B1:C12.
Select Insert -> Pivot Table
In the Create Pivot Table dialog, make sure "New Worksheet" is selected, and click OK.
On the Pivot Table, place "User" field in Rows, and "Total" field in Values. Select the Value Field Settings... for the "Total" field and make sure it uses Sum.
In the Pivot Table, select the Row Labels drop down, Label Filters, Greater Than... . Type "" into the dialog and select OK.
From Pivot Table Tools -> Analyze, select PivotChart. Choose Bar Chart from the dialog.
Below is a screen shot of the Pivot Table.
On the tab with the data, change the last potential user from E to G. On the Pivot Table, refresh the data.
Below is a screen shot of the refreshed pivot table.
SOLUTION 2: Use VBA to hide empty rows
The below code is attached to a button made visible on the worksheet. (n.b. There are other ways to activate the code such as change events, but this will depend on a number of factors outside the scope of this answer).
Sub HideRows()
Dim AllCatRange As Range
Set AllCatRange = Worksheets("Sheet1").Range("B2:B12")
Dim iLoop As Long
For iLoop = 1 To AllCatRange.Rows.Count
If AllCatRange.Cells(iLoop, 1) = "" Then
AllCatRange.Cells(iLoop, 1).EntireRow.Hidden = True
Else
AllCatRange.Cells(iLoop, 1).EntireRow.Hidden = False
End If
Next iLoop
Set AllCatRange = Nothing
End Sub
Below is a screen shot of the data tab with the button added.
After clicking the button, it now looks like this ...
This code will expand rows when they contain data, and collapse rows when they do not.
A potential problem with this approach is that expanding/collapsing rows will change the size of the chart, if the chart lays over those rows.
SOLUTION 3: Use VBA to modify the chart
The below code is attached to a button, and used to modify the chart after the source data is changed.
Sub ModifyChart()
Dim AllCatRange As Range
Set AllCatRange = Worksheets("Sheet1").Range("B2:B12")
Dim lastRow As Long
lastRow = 1
Dim iLoop As Long
For iLoop = 1 To 11
If AllCatRange.Cells(iLoop, 1) <> "" Then
lastRow = lastRow + 1
End If
Next iLoop
Dim PlotCatRange As Range
Set PlotCatRange = Worksheets("Sheet1").Range("B2:B" & lastRow)
Dim PlotSerRange As Range
Set PlotSerRange = Worksheets("Sheet1").Range("C2:C" & lastRow)
Worksheets("Sheet1").ChartObjects(1).Chart.FullSeriesCollection(1).XValues = "=Sheet1!" & PlotCatRange.Address
Worksheets("Sheet1").ChartObjects(1).Chart.FullSeriesCollection(1).Values = "=Sheet1!" & PlotSerRange.Address
Set AllCatRange = Nothing
Set PlotCatRange = Nothing
Set PlotSerRange = Nothing
End Sub
Below is a screenshot of the data tab with the new button in place.
Below is a screenshot of the tab after clicking the button.
Wrap up
My personal preference is to use VBA to modify the chart after the data is modified, as this reflects what you would do manually.
I found a solution! Thanks largely to this page. Here's what I did:
I created made a named range using this formula: =OFFSET(Report!$B$1,1,0,COUNTIF(Report!$B$2:$B$30,"<>-"),1)
For that to work, I had to change all the empty cells to output "-" when empty in stead of "". I couldn't get COUNTIF to accept "<>""" or "<>" or any other weird tricks I tried. Using "-" was easier.
That makes a dynamic named range that changes size as I put data into the sheet. I named a similar range for everything I'm trying to chart (Approved Status, Call Ready, Not Ready). The chart was able to accept those names and now it's dynamically sized. If I only have three agents on the sheet, it shows three huge bars. With twenty, it shows twenty bars (exactly what I was looking for).
One other tip: I changed my first row to output "" when empty, so that COUNTIF(Report!$B$2:$B$30,"<>-") always returns at least 1 (otherwise you get annoying errors because you have a named range referencing a 0-length array).

Excel VBA Pass Id of Value in Combobox

Looking for advice on the best way to pass an Id of a value into a parameter in excel VBA.
Essentially I'm trying to replicate getting the value rather than the text itself like for example in html:
<option value="1">Option one</option>
Would return 1. I could concatenate the Id to the start or end of the string with something like:
.additem varList(0, 1) & " | " & varList(1, 1)
But I'm looking for a 'cleaner' option if that makes sense?
Cheers
Create your combobox with at least 2 columns. This can be set using the ColumnCount property, via the VBE or through VBA code.
You can then adjust the ColumnWidths property to make one of the columns a width of 0 so it will not be displayed/visible to users.
WHen you populate the combobox, simply put the ID in one column of the ComboBox, and put the value in the other visible column. The interface will look like this, unless you adjust the columnwidths
Use the BoundColumn of the ComboBox to return the appropriate value, or you can do some iteration over the selected item(s) and refer to the indexed position:
Debug.Print Me.ComboBox.List(0, 0) '# Display the first row item in the first column
Debug.Print Me.ComboBox1.List(0, 1) '# Display the first row item in the SECOND column

PivotTable's Report Filter using "greater than"

I have a pivot table which has one of the fields (Probability) in a Report Filter. Its values are percentages in step of 5 (0,5,10,15,...,100). I'd like to use it to filter probabilities greater than or equal a certain value, but the filter only filters exact choices.
For now I use a workaround of allowing multiple values, and then selecting all values from the threshold I want, all the way to 100
This solution, apart from being awkward, doesn't show my selection, which is necessary as this table is being printed out.The Filter's display value is "(Multiple Values)" and I'd like to show all the values selected, or even better, something like ">=20%". I don't really care if they show in the field itself or in another cell outside the Pivot table.
My questions:
1) can I get the filter to filter on >= of my seletion? If not
2) Can I show the multiple selections like ">=20%"
I know this is a bit late, but if this helps anybody, I think you could add a column to your data that calculates if the probability is ">='PivotSheet'$D$2" (reference a cell on the pivot table sheet).
Then, add that column to your pivot table and use the new column as a true/false filter.
You can then change the value stored in the referenced cell to update your probability threshold.
If I understood your question right, this may get you what you wanted. The filter value would be displayed on the sheet with the pivot and can be changed to suit any quick changes to your probability threshold. The T/F Filter can be labeled "Above/At Probability Threshold" or something like that.
I've used this to do something similar. It was handy to have the cell reference on the Pivot table sheet so I could update the value and refresh the pivot to quickly modify the results. The people I did that for couldn't make up their minds on what that threshold should be.
In an Excel pivot table, you are correct that a filter only allows values that are explicitly selected. If the filter field is placed on the pivot table rows or columns, however, you get a much wider set of Label Filter conditions, including Greater Than. If you did that in your case, then the added benefit would be that the various probability levels that match your condition are shown in the body of the table.
One way to do this is to pull your field into the rows section of the pivot table from the Filter section. Then group the values that you want to keep into a group, using the group option on the menu. After that is completed, drag your field back into the Filters section. The grouping will remain and you can check or uncheck one box to remove lots of values.
After some research I finally got a VBA code to show the filter value in another cell:
Dim bRepresentAsRange As Boolean, bRangeBroken As Boolean
Dim sSelection As String
Dim tbl As Variant
bRepresentAsRange = False
bRangeBroker = False
With Worksheets("Forecast").PivotTables("ForecastbyDivision")
ReDim tbl(.PageFields("Probability").PivotItems.Count)
For Each fld In .PivotFields("Probability").PivotItems
If fld.Visible Then
tbl(n) = fld.Name
sSelection = sSelection & fld.Name & ","
n = n + 1
bRepresentAsRange = True
Else
If bRepresentAsRange Then
bRepresentAsRange = False
bRangeBroken = True
End If
End If
Next fld
If Not bRangeBroken Then
Worksheets("Forecast").Range("ProbSelection") = " >= " & tbl(0)
Else
Worksheets("Forecast").Range("ProbSelection") = Left(sSelection, Len(sSelection) - 1)
End If
End With
Maybe in your data source add a column which does a sumif over all rows.
Not sure what your data looks like but something like =(sumif([column holding pivot row heads),[current row head value in row], probability column)>.2).
This will give you a True when the pivot table will show >20%.
Then add a filter on your pivot table on this column for TRUE values
I can't say how much this might help you, but just found a solution to something similar problem which I faced.
In the Pivot-
Right click and choose Pivot table options
Choose the display option
uncheck the first 'Show expand/Collapse buttons'
check the 'Classic PivotTable Layout(enables dragging of fields in the grid)
click ok.
This would refine the data. Then, I had just copy and pasted this data in a new tab wherein I had applied the filters to my Total column with values greater than certain percentage.
This did work in my case and hope it helps you too.
Use a value filter. Click the dropdown arrow next to your Row Labels and you'll see a choice between Sort A to Z, Label Filters, and Value Filters. Selecting a Greater Than value filter will let you choose which column to use to filter out rows, even if that column has no dropdown arrow itself.

Resources