Hi,
I'm using Excel 2003 for this project.
For example: -
I have 3 pivot tables with the same row 'Media' (TV, Neswpaper, Magazine) and each has count as the data field (number of people who read/watch) then each pivot has a different column, Region, age, ***.
If I change the row of the first pivot to only show TV, how do i code it so pivot 2 and 3 only show TV also? I may have upto 20 pivots inthe end.
I found the below code to do this for page fields but can't adapt to rows - any suggestions please?
HTML Code:Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable) Dim ptTable As PivotTable, ptItem As PivotItem, vFields As Variant, lngField As Long On Error GoTo ExitPoint vFields = Array("filed1", "field2", "field3") Application.EnableEvents = False For lngField = LBound(vFields) To UBound(vFields) Step 1 For Each ptTable In ActiveSheet.PivotTables If ptTable <> Target Then With ptTable.PivotFields(vFields(lngField)) .CurrentPage = Target.PivotFields(vFields(lngField)).CurrentPage.Value End With End If Next ptTable Next lngField ExitPoint: Application.EnableEvents = True End Sub
Perhaps:
If not, post a sample.Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable) Dim ptTable As PivotTable, ptItem As PivotItem On Error GoTo ExitPoint Application.EnableEvents = False For Each ptTable In ActiveSheet.PivotTables If ptTable <> Target Then With ptTable.PivotFields("Media") For Each ptItem In .PivotItems ptItem.Visible = Target.PivotFields("Media").PivotItems(ptItem.Value).Visible Next ptItem End With End If Next ptTable ExitPoint: Application.EnableEvents = True End Sub
My Recommended Reading:
Volatility
Sumproduct & Arrays
Pivot Intro
Email from XL - VBA & Outlook VBA
Function Dictionary & Function Translations
Dynamic Named Ranges
Sweet, I see the logic and will try and implement tomorrow and post the result in an example.
Thanks DonkeyOte![]()
Right,
I get your code working great, but now I need to be able to add in more row field to my pivots, Eg Source and time.
I've tried combining the 2 sets of code above to achieve this but cannot get it to work.
Is it possible to adapt this so it only happens on a button click too please? I have other macros to add in more data fields and when i run these the code below runs and slows things down.
Cheers for your help - it's already beyond what I thought would be possible!
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable) Application.ScreenUpdating = False Dim ptTable As PivotTable, ptItem As PivotItem, vFields As Variant, lngField As Long On Error GoTo ExitPoint vFields = Array("Media", "Source","Time") Application.EnableEvents = False For lngField = LBound(vFields) To UBound(vFields) Step 1 For Each ptTable In ActiveSheet.PivotTables If ptTable <> Target Then With ptTable.PivotFields(vFields(lngField)) For Each ptItem In .PivotItems ptItem.Visible = Target.PivotFields(vFields).PivotItems(ptItem.Value).Visible Next ptItem End With End If Next ptTable Next lngField ExitPoint: Application.EnableEvents = True Application.ScreenUpdating = True End Sub
Please post a small sample file which illustrates all PT's in place and their respective structures. I won't look at it today unfortunately but will do so tomorrow.I've tried combining the 2 sets of code above to achieve this but cannot get it to work.
I would argue it's better to keep this tied to the PT event itself else PT functionality and results become detached which would be odd... better to toggle events in your "other" code as and where appropriate such that the PT update event does not fire in first instance.Is it possible to adapt this so it only happens on a button click too please? I have other macros to add in more data fields and when i run these the code below runs and slows things down.
My Recommended Reading:
Volatility
Sumproduct & Arrays
Pivot Intro
Email from XL - VBA & Outlook VBA
Function Dictionary & Function Translations
Dynamic Named Ranges
Right then, another stage on: -
Putting the code:
at the start and end of my 'other' Macros means the Private Sub Worksheet_PivotTableUpdate doesn't make them take ages to run. Thanks for the advice DonkeyOte.Application.EnableEvents = False Application.EnableEvents = True
I've added a little test workbook with where I'm up to. In my eyes there are only 3 issues remaining that prevent this from being a pretty darn good bit of code.
1) I can't get it to work for hiding page field items "(Multiple Items)" (e.g. right click month_trial_begin - field setting and hide Jan-09). This will not be automatically applied to the 2nd pivot table on the right
2) As in my previous thread I can't seem to get the row field 'guide' to update automatically on the 2nd pivot when i update the first to show 'how to' only for example. I've tried adding it in as an array but I messed up the entire macro some howso took it out
3) I'm sure the marco can be cleaned up more to make it more effiecient (although it is still pretty quick on my full version that is 65mb in size)
Well, this is already much better than I could have ever got it on my own, so many thanks.
Regards
Davellll
PS sorry its taken me so long to post this reply - me bad.
Just quickly... you make reference to "multiple page field items" which leads me to believe you're running this in XL2007 specifically - is that correct ?
If so it would IMO be best to upload a 2007 file ... reason being PT functionality differs between 2007 and earlier versions - for example pre XL2007 there is no multi select Page filter functionality ... as such the VBA for controlling PT's will differ between versions and it makes sense to code exclusively in the version you're using. Of course if it's your intention to use the file in multiple versions then it's a different matter.
My Recommended Reading:
Volatility
Sumproduct & Arrays
Pivot Intro
Email from XL - VBA & Outlook VBA
Function Dictionary & Function Translations
Dynamic Named Ranges
davellll,
here is some code that should work if (and only if) the Pivots are created in XL2007 file (ie not in compatibility mode)
In the above code I have as per your code comments consolidated all of the fields into the one process (ie Page Fields and Row Fields)Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable) Dim ptTable As PivotTable, ptField As PivotField, ptItem As PivotItem Dim vFields As Variant, lngField As Long, boolMulti As Boolean On Error GoTo ExitPoint Application.ScreenUpdating = False Application.EnableEvents = False vFields = Array("Customer", "Month_Trial_Began", "Payment_Interval", "Media", "Guide") For lngField = LBound(vFields) To UBound(vFields) Step 1 For Each ptTable In ActiveSheet.PivotTables If ptTable <> Target Then Set ptField = ptTable.PivotFields(vFields(lngField)) Select Case ptField.Orientation Case xlRowField For Each ptItem In ptField.PivotItems ptItem.Visible = Target.PivotFields(vFields(lngField)).PivotItems(ptItem.Value).Visible Next ptItem Case xlPageField boolMulti = Target.PivotFields(vFields(lngField)).EnableMultiplePageItems With ptField .ClearAllFilters Select Case boolMulti Case False .CurrentPage = Target.PivotFields(vFields(lngField)).CurrentPage.Value Case True .CurrentPage = "(All)" For Each ptItem In Target.PivotFields(vFields(lngField)).PivotItems .PivotItems(ptItem.Name).Visible = ptItem.Visible Next ptItem .EnableMultiplePageItems = boolMulti End Select End With boolMulti = False End Select Set ptField = Nothing End If Next ptTable Next lngField ExitPoint: Application.EnableEvents = True Application.ScreenUpdating = True End Sub
I've attached a working example (.xlsm as outlined above)
EDIT:
I should add you can still use multi page item functionality in PT's created in earlier versions but it requires moving the Page Field to the Row Field, making selections, moving back to Page Field... invariably be a pain.
If running earlier PT's in 2007 you can use MultiPageField selection but in code terms the visible status of the pivot items in the page field is not available to the other PT's ... so to mimic you would need to (I suspect) action the above
(ie move the field in both Target and subsequent PT's from Page to Row to ensure appropriate selections and then move back to Page Field
Last edited by DonkeyOte; 10-30-2009 at 07:53 AM.
My Recommended Reading:
Volatility
Sumproduct & Arrays
Pivot Intro
Email from XL - VBA & Outlook VBA
Function Dictionary & Function Translations
Dynamic Named Ranges
Hi,
I think this may just be me using the wrong terminology as I'm deff using 2003 for this (although I may need to adjust code for future products into 2007 this project is purely 2003).
I've attached a screenshot of what i am calling 'multiple items', it is actually really hiding items but in the page field you do this for the text (multiple items) is displayed. in the example I am hiding Jan 09 so only Feb 09 is displayed, in the full version the user may wish to view the latest 3, 4, 6 or 12 months only.
I hope this makes sense?
Regard
Davellll
Ah ok... I understand now - that makes sense... in my (late) edit to prior post I think I highlighted the issue you will face on that front... I'm in the midst of something else right now but I will run some tests on another client machine on which I'm running 2002 and I'll see what I can come up with re: hidden page items and replication thereof.
As things stand I think you can in essence disregard the prior code for the time being (unfortunately).
My Recommended Reading:
Volatility
Sumproduct & Arrays
Pivot Intro
Email from XL - VBA & Outlook VBA
Function Dictionary & Function Translations
Dynamic Named Ranges
Ignoring issue of multi page item filters for the time being the below should do the remainder... ie can replace your existing code.
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable) Dim ptTable As PivotTable, ptField As PivotField, ptItem As PivotItem Dim vFields As Variant, lngField As Long, boolMulti As Boolean On Error GoTo ExitPoint Application.ScreenUpdating = False Application.EnableEvents = False vFields = Array("Customer", "Month_Trial_Began", "Payment_Interval", "Media", "Guide") For lngField = LBound(vFields) To UBound(vFields) Step 1 For Each ptTable In ActiveSheet.PivotTables If ptTable <> Target Then Set ptField = ptTable.PivotFields(vFields(lngField)) Select Case ptField.Orientation Case xlRowField For Each ptItem In ptField.PivotItems ptItem.Visible = Target.PivotFields(vFields(lngField)).PivotItems(ptItem.Value).Visible Next ptItem Case xlPageField ptField.CurrentPage = Target.PivotFields(vFields(lngField)).CurrentPage.Value End Select Set ptField = Nothing End If Next ptTable Next lngField ExitPoint: Application.EnableEvents = True Application.ScreenUpdating = True End Sub
My Recommended Reading:
Volatility
Sumproduct & Arrays
Pivot Intro
Email from XL - VBA & Outlook VBA
Function Dictionary & Function Translations
Dynamic Named Ranges
Not sure how badly this will perform but the below works on my 2002 version and will cater for multiple page item selection etc...
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable) Dim ptTable As PivotTable, ptField As PivotField, ptFieldT As PivotField, ptItem As PivotItem Dim vFields As Variant, lngField As Long, boolMulti As Boolean On Error GoTo ExitPoint Application.ScreenUpdating = False Application.EnableEvents = False vFields = Array("Customer", "Month_Trial_Began", "Payment_Interval", "Media", "Guide") For lngField = LBound(vFields) To UBound(vFields) Step 1 For Each ptTable In ActiveSheet.PivotTables If ptTable <> Target Then Set ptField = ptTable.PivotFields(vFields(lngField)) Set ptFieldT = Target.PivotFields(vFields(lngField)) Select Case ptFieldT.Orientation Case xlRowField For Each ptItem In ptField.PivotItems ptItem.Visible = Target.PivotFields(vFields(lngField)).PivotItems(ptItem.Value).Visible Next ptItem Case xlPageField With ptFieldT ptField.CurrentPage = .CurrentPage.Value If .CurrentPage.Value = "(All)" Then .Orientation = xlRowField For Each ptItem In ptField.PivotItems ptItem.Visible = .PivotItems(ptItem.Name).Visible Next ptItem .Orientation = xlPageField .Position = Target.PageFields.Count - lngField End If End With End Select Set ptField = Nothing Set ptFieldT = Nothing End If Next ptTable Next lngField ExitPoint: Application.EnableEvents = True Application.ScreenUpdating = True End Sub
My Recommended Reading:
Volatility
Sumproduct & Arrays
Pivot Intro
Email from XL - VBA & Outlook VBA
Function Dictionary & Function Translations
Dynamic Named Ranges
Thanks DonkeyOte, it certainly works in my example - I've added a 3rd pivot table and it works fine (attached). Problem now is that I only 75% understand what the macro is doing so when I add it to my proper Excel 2003 project it doesn't work (well, i can't get it to). Is there a way I can give you a version of my main project to look at please? - what happens in that is where the first pagefield =(All) gets converted to a row and stays there.
I initially simply tried updating the array in my main project to be
but failed - are there certain table options or something that need to be enabled?vFields = Array("Customer", "Month_Trial_Began", "Payment_Interval","pagefield4","pagefield5 "Media", "Guide","row3")
No real worries if I can't sort it as this is all way beyond my VB ability already, but as it's all got this far it would be a bit of a shame to not finish the marathon one step from the line.
The code in the example with my comments is -
Private Sub Worksheet_PivotTableUpdate(ByVal Target As PivotTable) 'sets what all the parameters are Dim ptTable As PivotTable, ptField As PivotField, ptFieldT As PivotField, ptItem As PivotItem ' Boolean is true or false i think? Dim vFields As Variant, lngField As Long, boolMulti As Boolean 'error handling goto line that says exitpoint if there is an error On Error GoTo ExitPoint 'turns screen updating off so user doesn't see 'flashing screen' Application.ScreenUpdating = False 'turns events off so macro runs smoothly and doesn't repeatedly update for every pivot item or table etc. Application.EnableEvents = False 'sets the array for all variable names in the page and row fields 'I'm not sure if the order of these is important but have kept the top page field first inthe array ' the second , second from top and so on then list the row fields in no specific order vFields = Array("Customer", "Month_Trial_Began", "Payment_Interval", "Media", "Guide") 'this runs the macro for each of the variables in the array from the first one to the last in order For lngField = LBound(vFields) To UBound(vFields) Step 1 ' runs it for all the pivot tables 1 at a time For Each ptTable In ActiveSheet.PivotTables 'will not run it for the pivot table you have just changed as this is already changed manually by you! If ptTable <> Target Then 'I see it sets the Dim ptfield to be equal to the first pivot table and thefirst field in the array ' but not sure why Set ptField = ptTable.PivotFields(vFields(lngField)) 'I see it sets the Dim ptfieldT to be equal to the pivot table you have manually chenged ' and the first field in the array but not sure why Set ptFieldT = Target.PivotFields(vFields(lngField)) 'not sure what these 2 lines do Select Case ptFieldT.Orientation Case xlRowField 'loops through all the pivot table items to see which ones are visible and somehow sets them to the same as 'the pivot table you changed manually. For Each ptItem In ptField.PivotItems ptItem.Visible = Target.PivotFields(vFields(lngField)).PivotItems(ptItem.Value).Visible Next ptItem 'Again not familiar with Case (assume this part only runs on pagefields) Case xlPageField 'with the field you changed in the pivot table do: - With ptFieldT ptField.CurrentPage = .CurrentPage.Value 'if the value of the field = (All) then do: - If .CurrentPage.Value = "(All)" Then 'change it to a row .Orientation = xlRowField ' then loop though to see which pivot items are visible and somehow sets them to the same as 'the pivot table you changed manually. For Each ptItem In ptField.PivotItems ptItem.Visible = .PivotItems(ptItem.Name).Visible Next ptItem 'change back a page field in the same position as before .Orientation = xlPageField .Position = Target.PageFields.Count - lngField End If End With End Select Set ptField = Nothing Set ptFieldT = Nothing End If ' This is the bit that makes all the other pivots the same as the one you change manually Next ptTable Next lngField ExitPoint: Application.EnableEvents = True Application.ScreenUpdating = True End Sub
I've had a look at this more thoroughly now and the problem is actually quite an obvious one but one that I did not account for in testing...namely items which exist in one PT may not exist in another ... and this could be caused by a number of issues...
a) values are simply different
b) legacy items exist in the item listings
Now point b) is an interesting one and one that made me think a little more about this... in general given you want to give each PT the most opportunity of being identical you need to ensure that when you refresh one PT you refresh them all... however the act of Refresh is different to making selections etc (ie the former is based on a premise that underlying data has altered) yet refreshing via the Refresh button will
1) only refresh the relevant cache
2) invoke the update event
given the possibility of problem a) we then need to try and restrict the opportunity for error by not invoking the update event on a standard refresh...how to do this ?
Well I thought about it and came up with the idea of disabling/creating some custom controls on the right-click PT menu, ie into ThisWorkbook:
and then into a new modulePrivate Sub Workbook_BeforeClose(Cancel As Boolean) Call ToggleControl("Query and Pivot", 459, True) End Sub Private Sub Workbook_Open() Call ToggleControl("Query and Pivot", 459, False) End Sub Private Sub Workbook_SheetBeforeRightClick(ByVal Sh As Object, ByVal Target As Range, Cancel As Boolean) Run "DeleteRC" Run "AddRC" End Sub
The code for the refresh All should for sake of clarity reside in a new & separate modulePrivate Module Public Sub ToggleControl(strCB As String, lngID As Long, boolStatus As Boolean) Application.CommandBars(strCB).FindControl(ID:=lngID).Enabled = boolStatus End Sub Public Sub AddRC() Dim ctrl As CommandBarControl With Application.CommandBars("PivotTable Context Menu") 'disable default refresh .FindControl(ID:=459).Enabled = False 'add refresh all icon Set ctrl = .Controls.Add(Type:=1, Before:=1) With ctrl .Caption = "Refresh All" .OnAction = "PTRefresh" End With Set ctrl = Nothing End With End Sub Public Sub DeleteRC() Dim ctrl As CommandBarControl For Each ctrl In Application.CommandBars("PivotTable Context Menu").Controls Select Case ctrl.Caption 'remove custom control Case "Refresh All" ctrl.Delete 'enable default control Case "Refresh" ctrl.Enabled = True End Select Next ctrl End Sub
What all of the above will do is ensure you can only refresh All PT's simultaneously via the right-click menu and "new" option added namely: Refresh All ... andPrivate Module Public Sub PTRefresh() Dim PT As PivotTable On Error GoTo ExitPoint Application.EnableEvents = False For Each PT In ActiveSheet.PivotTables With PT.PivotCache .MissingItemsLimit = xlMissingItemsNone .Refresh End With Next PT ExitPoint: Application.EnableEvents = True End Sub
when this is done the BeforeUpdate event is NOT invoked (it will also remove "legacy" items from any/all lists)
Now the above still does not resolve the problem that given the data sources for each PT are different it's still quite possible that items will exist in one PT but not in another, for ex. you may run an Edit->Replace on all which columns but forget to update the last one meaning the same options do not exist in all PT's ... this may be highly unlikely however it is still a possibility.
On that basis you then need to think about adding handlers around the setting of item visibility so I would change the existing Select Case section to
And all of that together *should* I think resolve your issue(s) ?Select Case ptFieldT.Orientation Case xlRowField On Error Resume Next For Each ptItem In ptField.PivotItems ptItem.Visible = Target.PivotFields(vFields(lngField)).PivotItems(ptItem.Value).Visible Next ptItem On Error GoTo ExitPoint Case xlPageField With ptFieldT On Error Resume Next ptField.CurrentPage = .CurrentPage.Value On Error GoTo ExitPoint If .CurrentPage.Value = "(All)" Then .Orientation = xlRowField On Error Resume Next For Each ptItem In ptField.PivotItems ptItem.Visible = .PivotItems(ptItem.Name).Visible Next ptItem On Error GoTo ExitPoint .Orientation = xlPageField .Position = Target.PageFields.Count - lngField End If End With End Select
Last edited by DonkeyOte; 11-03-2009 at 09:38 AM.
My Recommended Reading:
Volatility
Sumproduct & Arrays
Pivot Intro
Email from XL - VBA & Outlook VBA
Function Dictionary & Function Translations
Dynamic Named Ranges
Beware!
I have found another little problem with this macro that is easy enough to fix. If your data has blanks in it the pivot table will have a pivot item called "(Blank)" in it, but of course your actual data is not "(Blank)" it is "". Therefore the other pivot tables will not update if you wish to hide/show blanks.
So my solution is to simply find and replace all "" with "(Blank)" within all the pivot tables data regions.
Also beware of sorting your various pivot tables differently as this will effect them being 'lined up' i found that sticking to a manual sort was the safest option for my data. Changing to ascending seemed to stop the macro working.
At last I can actually contribute a little something rather than asking Qs!
DaveL
Last edited by davellll; 11-13-2009 at 11:48 AM.
There are currently 1 users browsing this thread. (0 members and 1 guests)
Bookmarks