snippetMinor
Show one sheet for each field in pivot filter field
Viewed 0 times
showfieldeachpivotsheetonefilterfor
Problem
Into my workbook I've the db of inventory, in another sheet I've one pvt and in the filter field I put the filed "Card". Now I want create one sheet for each "Card" usually I use this code for this operation:
This function always runs right, but this year the sheets are much (175) and my code stopped for exceeded memory. I think to hide half off the total sheets, if I hide the sheets manually with pvt filer the operation run in a couple of minutes, if I use this code I need 9 minutes.
Can somebody help me to improve the performance my code?
Sub cmdg2b2(control As IRibbonControl) 'genera una sheets per ogni card
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Sheets("pivot").Select
ActiveSheet.Unprotect
ActiveSheet.PivotTables("Pvt_Pck").ShowPages PageField:="Card"
Sheets("pivot").Protect DrawingObjects:=True, Contents:=True, Scenarios:=True
Sheets("pivot").EnableSelection = xlNoSelection
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End SubThis function always runs right, but this year the sheets are much (175) and my code stopped for exceeded memory. I think to hide half off the total sheets, if I hide the sheets manually with pvt filer the operation run in a couple of minutes, if I use this code I need 9 minutes.
Can somebody help me to improve the performance my code?
Function ShowSheets(ByVal conta As String) 'card serves to define whether to show the first half of the sheets or the second
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim tsheet As Integer
Dim tstart As Date, tend As Date, tteempo As Date
tstart = Now()
ActiveSheet.PivotTables("Pvt_Pck").PivotFields("Card").CurrentPage = "(All)"
With ActiveSheet.PivotTables("Pvt_Pck").PivotFields("Card")
tsheet = Int(.PivotItems.Count / 2)
Select Case conta
Case "A"
For i = 1 To tsheet
.PivotItems(i).Visible = False
Next i
Case "B"
For i = tsheet + 1 To .PivotItems.Count - 1
.PivotItems(i).Visible = False
Next i
End Select
End With
Range("A4").Select
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
tend = Now()
ttempo = tend - tstart
Debug.Print ttempo
End FunctionSolution
Sheets("pivot").Select
ActiveSheet.Unprotect
ActiveSheet.PivotTables("Pvt_Pck").ShowPages PageField:="Card"
Sheets("pivot").Protect DrawingObjects:=True, Contents:=True, Scenarios:=True
Sheets("pivot").EnableSelection = xlNoSelectionThis is rather inefficient, and harder to follow than it needs to be. There's no need to
Select anything and to work off the ActiveSheet.Every sheet has a code name, an identifier that you can use in code to refer to it. Think of it as a free object instance living in global scope, waiting to be used. Select the worksheet in the VBE's Project Explorer (Ctrl+R if it's not displayed), and look at its properties (F4 if it's not displayed):
Notice there are two
Name properties: one (Name) is the name of the tab in Excel; the other ((Name)) is the name of that "free object" I'm talking about.If
Sheets("pivot") is named Sheet1, then you can .Select it like this:Sheet1.SelectBut you don't need to do this.
You're accessing the
Sheets collection (implicitly ActiveWorkbook.Sheets); that collection contains all sheets, and in Excel not all Sheet objects are a Worksheet - the Sheets collection also contains Chart sheets!So instead of
Sheets("name"), you should be accessing Worksheets("name") if you expect to be working with a worksheet. But you don't need to do that either.And you should avoid implicit references to the
ActiveWorkbook or the ActiveSheet: a fully-qualified ThisWorkbook.Worksheets("name") call is more reliable than Worksheets("name").But I digress. I was saying you don't need to do this. Let's say you gave your
"pivot" sheet a code name, and that you can access it in code with the PivotSheet identifier.You could then do this:
With PivotSheet
.Unprotect
.PivotTables("Pvt_Pck").ShowPages PageField:="Card"
.Protect DrawingObjects:=True, Contents:=True, Scenarios:=True
.EnableSelection = xlNoSelection
End WithThis does exactly the same thing as the snippet I quoted at the top of this post. Except if you look under the hood. Your code really did this:
- In the
Sheetscollection of theActiveWorkbook, look for the sheet named"pivot", andSelectthat sheet (so it becomes theActiveSheet).
Unprotectthe active sheet.
- In the
PivotTablescollection of theActiveSheet, look for the pivot table named"Pvt_Pck", and change thePageFieldto"Card".
- In the
Sheetscollection of theActiveWorkbook, look for the sheet named"pivot", andProtectthat sheet.
- In the
Sheetscollection of theActiveWorkbook, look for the sheet named"pivot", and setEnableSelectionon that sheet toxlNoSelection.
Notice everytime you access the
Sheets collection, you're working much harder than you need to. Compare to mine:- Using the
PivotSheetobject reference...
Unprotectit.
- In the
PivotTablescollection, look for the pivot table named"Pvt_Pck", and change thePageFieldto"Card".
Protectit.
- Set
EnableSelectiontoxlNoSelection.
Sub cmdg2b2(control As IRibbonControl)You should always use names that you can pronounce.
cmdg2b2 says nothing more than can be inferred from its IRibbonControl parameter, that it's some Ribbon callback procedure. What does it do? Wouldn't this be easier to work with?Public Sub SetPivotPageField(ByRef control As IRibbonControl)The procedure now...
- has an explicit access modifier (
Public)
- is now explicit about how it's receiving its parameter (
ByRef)
- has a name that starts with a verb, conforms to the
PascalCasenaming convention of everything else in VBA, and gives us a clue about what's happening in its scope, without having to look at the actual code.
In the
ShowSheets method, I can't help but notice these repeated lines:Application.ScreenUpdating = False
Application.Calculation = xlCalculationManualYou had them in your Ribbon callback procedure too:
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManualEvery time you set
Application.Calculation to xlCalculationAutomatic, you trigger a recalculation; in a large complicated workbook with lots of lookups and external dependencies, this single instruction alone can take several minutes to complete: don't toggle calculation mode without needing to.Now, repeating code like this isn't something you want to have all over the place. Make a small procedure for that.
Private Sub ToggleWaitMode(Optional ByVal wait = True, Optional ByVal toggleCalculation = True, Optional ByVal toggleEvents = True)
With Application
.StatusBar = IIf(wait, "Please wait...", vbNullString)
.ScreenUpdating = Not wait
.EnableEvents = IIf(toggleEvents, Not wait, .EnableEvents)
.Calculation = IIf(toggleCalcualtion, IIf(wait, xlCalculationManual, xlCalculationAutomatic), .Calculation)
End With
End SubNow instead of copying the s
Code Snippets
Sheets("pivot").Select
ActiveSheet.Unprotect
ActiveSheet.PivotTables("Pvt_Pck").ShowPages PageField:="Card"
Sheets("pivot").Protect DrawingObjects:=True, Contents:=True, Scenarios:=True
Sheets("pivot").EnableSelection = xlNoSelectionSheet1.SelectWith PivotSheet
.Unprotect
.PivotTables("Pvt_Pck").ShowPages PageField:="Card"
.Protect DrawingObjects:=True, Contents:=True, Scenarios:=True
.EnableSelection = xlNoSelection
End WithSub cmdg2b2(control As IRibbonControl)Public Sub SetPivotPageField(ByRef control As IRibbonControl)Context
StackExchange Code Review Q#119628, answer score: 2
Revisions (0)
No revisions yet.