Dynamic Chart using Pivot Table and VBA
by Jon Peltier
Peltier Technical Services, Inc., Copyright © 2009.
Licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License.
In Using Pivot Table Data for a Chart with a Dual Category Axis, I showed how to make a Chart with a Dual Category Axis using a pivot table to properly arrange the source data. I generally prefer using a regular chart, because pivot charts are pretty inflexible when it comes to formatting. Unfortunately, a regular chart doesn’t know when the pivot table has been resized by a refresh. In Dynamic Chart using Pivot Table and Range Names I showed how to use dynamic ranges to allow a regular chart to update when the pivot table changes. However, that technique only accommodates a change in the number of points in a series, not the number of series in the chart. To allow for changing height and width of a pivot table, you either need to revert to a pivot chart, or use a VBA procedure, which I describe below.
This example begins with the same data range, pivot table, and chart as the previous ones.
| A | B | C | D | |
| 1 | Main Category | Category | Sub Category | Defects |
| 2 | Mechanical | Mechanical | Gear | 11 |
| 3 | Mechanical | Mechanical | Bearing | 8 |
| 4 | Mechanical | Mechanical | Motor | 3 |
| 5 | Electrical | Electrical | Switch | 19 |
| 6 | Electrical | Electrical | Plug | 12 |
| 7 | Electrical | Electrical | Cord | 11 |
| 8 | Electrical | Electrical | Fuse | 3 |
| 9 | Electrical | Electrical | Bulb | 2 |
| 10 | Hydraulic | Hydraulic | Pump | 4 |
| 11 | Hydraulic | Hydraulic | Leak | 3 |
| 12 | Hydraulic | Hydraulic | Seals | 1 |


I’ve highlighted some sections of the pivot table above to correlate them with pivot table ranges in the VBA object model.
| Color | Range |
| Blue | DataBodyRange |
| Green | ColumnRange |
| Purple | RowRange |
| Yellow | DataLabelRange |
The VBA procedure is not too complicated. It should be run whenever the pivot table has been refreshed. It can be called from a button, or from a Worksheet_Calculate event procedure. Whatever works best in a particular application. Paste this code into a regular code module:
Sub UpdateChartFromPivot()
Dim rCategories As Range
Dim rValues As Range
Dim rSeriesNames As Range
Dim pt As PivotTable
Dim cht As Chart
Dim iSeries As Long
Dim nSeries As Long
' Define the Pivot Table
'Set pt = ActiveSheet.PivotTables(1)
Set pt = ActiveSheet.PivotTables("PT_ChartSource")
' Define the Ranges
Set rValues = pt.DataBodyRange
With pt.RowRange
Set rCategories = .Offset(1).Resize(.Rows.Count - 1)
End With
Set rSeriesNames = pt.ColumnRange.Rows(2)
' Define the Chart
'Set cht = ActiveSheet.ChartObjects(1).Chart
Set cht = ActiveSheet.ChartObjects("chtPivotData").Chart
' How Many Series?
nSeries = rSeriesNames.Columns.Count
' Remove or Add Series until Chart Has Correct Number
Select Case cht.SeriesCollection.Count - nSeries
Case Is > 0
' too many: remove excess series
For iSeries = nSeries To cht.SeriesCollection.Count + 1
cht.SeriesCollection(iSeries).Delete
Next
Case Is < 0
' too few: add sufficient series
For iSeries = cht.SeriesCollection.Count + 1 To nSeries
cht.SeriesCollection.NewSeries
Next
Case Else
' just right
End Select
' Populate Each Series
For iSeries = 1 To nSeries
With cht.SeriesCollection(iSeries)
.Name = rSeriesNames.Columns(iSeries)
.Values = rValues.Columns(iSeries)
.XValues = rCategories
.Border.LineStyle = xlNone
End With
Next
End Sub
Let’s add some rows to the pivot table source data:
| A | B | C | D | |
| 1 | Main Category | Category | Sub Category | Defects |
| 2 | Mechanical | Mechanical | Gear | 11 |
| 3 | Mechanical | Mechanical | Bearing | 8 |
| 4 | Mechanical | Mechanical | Motor | 3 |
| 5 | Electrical | Electrical | Switch | 19 |
| 6 | Electrical | Electrical | Plug | 12 |
| 7 | Electrical | Electrical | Cord | 11 |
| 8 | Electrical | Electrical | Fuse | 3 |
| 9 | Electrical | Electrical | Bulb | 2 |
| 10 | Hydraulic | Hydraulic | Pump | 4 |
| 11 | Hydraulic | Hydraulic | Leak | 3 |
| 12 | Hydraulic | Hydraulic | Seals | 1 |
| 13 | Environmental | Environmental | Emissions | 5 |
| 14 | Environmental | Environmental | Noise | 6 |
| 15 | Environmental | Environmental | Thermal | 2 |
Now update the pivot table:

And finally, run the VBA procedure to update the chart:

The VBA procedure can be run from the Macros dialog (press Alt+F8 or navigate the menu/ribbon), from a button you’ve placed on the sheet (see Assign a Macro to a Button or Shape and Assign a Macro to an ActiveX Control), from a menu item (see Assign a Macro to a Toolbar or Menu), or from a worksheet event procedure.
In Excel 2003 (and maybe 2002) you can use the Worksheet_PivotTableUpdate event procedure. Select the sheet with the pivot table and chart, right click on the sheet tab, and choose View Code. This opens the code module for the active worksheet. From the left dropdown in the module window, choose Worksheet, and from the right dropdown, choose PivotTableUpdate. The shell for the event procedure is entered into the module for you. Insert the procedure name as shown below, and you’re ready to go:
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable)
UpdateChartFromPivot
End Sub
If you’re using an older version of Excel, there is no PivotTableUpdate event. What I do in this case is place a formula in a cell outside the pivot table (say, M1) with a formula like:
=SUM(A1:J20)
where the range A1:J20 includes the pivot table. This formula ensures that a calculation occurs when the pivot table refreshes. Then I use the Worksheet_Calculate event procedure to kick off the chart update procedure. As above, select the sheet with the pivot table and chart, right click on the sheet tab, and choose View Code. This opens the code module for the active worksheet. From the left dropdown in the module window, choose Worksheet, and from the right dropdown, choose Calculate. The shell for the event procedure is entered into the module for you. Insert the procedure name as shown below, and you’re ready to go:
Private Sub Worksheet_Calculate()
UpdateChartFromPivot
End Sub
Fourth in a series
- Chart with a Dual Category Axis
- Using Pivot Table Data for a Chart with a Dual Category Axis
- Dynamic Chart using Pivot Table and Range Names
- Dynamic Chart using Pivot Table and VBA
Possibly Related Posts:
- Dynamic Chart using Pivot Table and Range Names
- Using Pivot Table Data for a Chart with a Dual Category Axis
- Update Regular Chart when Pivot Table Updates
- Chart with a Dual Category Axis
- Extract Chart Data
- VBA to Split Data Range into Multiple Chart Series
- VBA Conditional Formatting of Charts by Series Name
- Extend Range to Add New Series (VBA)
- How To: Assign a Macro to an ActiveX Control
- Regular Charts from Pivot Tables
Posted: Friday, April 25th, 2008 under Dynamic Charts.
Comments: 17
Comments
Comment from Peder Schmedling
Time: Saturday, April 26, 2008, 9:42 am
Hey!
Way to go, very nice solution.
I love this blog, keep up the good work.
Comment from Jon Peltier
Time: Saturday, April 26, 2008, 12:29 pm
Peder -
I told you it was on the way! Actually I was writing it as your previous comment came in. The nice thing about the blog is I can write about smaller parts of a problem, and post a bit at a time, like chapters in a serial novel. With the regular web site, I felt I had to write the whole novel at once, so I often just never got around to it.
Comment from Subhash Tiwari
Time: Friday, June 6, 2008, 11:29 am
Hi Team,
It’s nice to see the effort you people doing to help the other person as well a new comer.
I have a query…I’m working on a data range where pivot item value need to change dynamically. Once I record the Marco. It’s given a static value..
Code is: -
With ActiveSheet.PivotTables(”PivotTable2″).PivotFields(”CLOSED_DTTM”)
.PivotItems(”6/4/2008″).Visible = False
End With
Here “6/4/2008″ is a current date & every day this value gets changes.
Please help me how can I change it dynamically in the VBA code
Comment from Jon Peltier
Time: Friday, June 6, 2008, 12:12 pm
Subhash -
This might be as easy as using today’s data in the proper format:
.PivotItems(Format(Now, “m/d/yyyy”)).Visible = False
Comment from Subhash Tiwari
Time: Monday, June 9, 2008, 9:20 am
Hi Jon ,
Thank for your advice. However it’s not working :-(
Even I tried Today & Date function as well…but in all cases curser stop at this line of codes & every time I need to put the values manually :-(
Comment from Jon Peltier
Time: Monday, June 9, 2008, 9:41 am
1. Note that the straight quotes in my line of code have been converted by WordPress into curly quotes.
2. Make sure that Format(Now, “m/d/yyyy”) exactly matches the pivot item label you need to hide. In the Immediate Window, type this and press Enter:
?Format(Now, “m/d/yyyy”)
Comment from Subhash Tiwari
Time: Monday, June 9, 2008, 11:52 am
Thanks a lot Jon :-)..now code is working fine
Comment from Felix Marusic
Time: Friday, January 16, 2009, 10:25 am
John,
I’m trying apply a dynamic chart title which will should change when the data is either filtered or sorted, but have been unable to do so using a macro or via the interface. Not sure of the VBA syntax needed to pull this off.
Comment from Jon Peltier
Time: Friday, January 16, 2009, 10:44 am
Have you tried the Worksheet_Calculate and Worksheet_PivotTableUpdate event procedures? These are on the worksheet’s code module. Easiest way to get there is to right click on the worksheet tab and select View Code.
Comment from Felix Marusic
Time: Friday, January 16, 2009, 5:30 pm
Jon,
Thanks I’ll look into it.
Felix
Comment from Doug Glancy
Time: Tuesday, March 3, 2009, 8:10 pm
Jon, I’m doing something similar with an addin that updates a workbook on its Worksheet_PivotTableUpdate event. It works great except that some, to all, of my charts (pie charts!) keep reverting to Pivot Charts. It seems like this didn’t happen when my code was in the workbook itself instead of in an addin. Also, the workbooks are actually created from templates if that makes any difference, and the pivot tables are based on external queries to Access. I can kind of prevent it if I put a Stop in the code and select a sheet, but only kind of, and putting in code to activate sheets doesn’t help. I can’t really see a pattern to which charts revert and which don’t.
I know, from you, that you can accidentally turn a regular chart into a pivot chart by hand, so I assume this is related.
Comment from Doug Glancy
Time: Tuesday, March 3, 2009, 9:54 pm
I got it. Looked at your code in this post some more and that gave me the answer. I was using the SetSourceData method (I think I got that from the macro recorder, but can’t swear to it). When I switched to SeriesCollection.XValues and .Values my charts started behaving. Thanks for your most helpful site!
Comment from Jon Peltier
Time: Tuesday, March 3, 2009, 10:06 pm
Doug -
Glad you got it sorted out. When SetSourceData overlaps with a pivot table, the chart is converted to a pivot chart. Inconvenient, but setting the X and Y values of each series maintains the independence of the chart. I’m working on a replacement chart source data dialog which bypasses SetSourceData in favor of series-by-series definitions.
Comment from Doug Glancy
Time: Tuesday, March 3, 2009, 10:32 pm
Jon, that sounds like a good project. I’ve got a nice pivot chart class that passes back references to items in a field and the like. It’s been a lot of fun to put together. Thanks again.
Comment from NMullis
Time: Tuesday, May 12, 2009, 4:07 pm
Jon, I have a workbook with several pivot charts. The charts are all Line chart types where each of the series lines represents a retailer and each of the 8 retailers may or may not be in the resulting pivot chart. On each chart I need there to be consistency among each of the retailer colors, that is, to see Retailer A always in yellow, Retailer B always in blue, Retailer C always in green, etc. If Retailer A is not represented on a given chart then another retailer should not be yellow.
To add to the complexity, I would like a way to update all pivot charts (12) at one time. Is this possible?
Thank you,
N
Comment from Jon Peltier
Time: Tuesday, May 12, 2009, 4:40 pm
For the consistent formatting, you could run a macro like that in VBA Conditional Formatting of Charts by Series Name after refreshing the pivot tables.
To refresh all of the pivot tables:
Sub RefreshAllPivots()
Dim pt As PivotTable
For Each pt In ActiveSheet.PivotTables
pt.RefreshTable
Next
End Sub
 :
Comment from NMullis
Time: Wednesday, May 13, 2009, 11:05 am
That is awesome, thank you! I had to adjust for having a line chart style and then all worked perfectly.
Thanks again! N























Write a comment
I welcome comments from my readers. If you have an opinion on this post, if you have a question or if there is anything to add, I want to hear from you. Whether you agree or disagree, please join the discussion.
If you want to include an image in your comment, post it on your own site or on one of the many free image sharing sites, and include a link in your comment. I'll download your image and insert the necessary html to display the image inline.
Read the PTS Blog Comment Policy.