VBA - How to set printarea for multiple pages in one sheet? - excel

I am using this code
Sub print_area()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
With ws.PageSetup
.PrintTitleColumns = "$A:$E"
.PrintArea = ws.Range("A128").Value
.CenterHorizontally = True
End With
Next
End Sub
Range("A128").Value has the value as this: $F$1:$AF$125,$AG$1:$BE$125,$BF$1:$CD$125,$CE$1:$DA$125,$DB$1:$DX$125
print areas are not set properly (the areas are close to set range but not as desired), what other parameters do I have to set to make it work right?
Red arrow indicates where the print area should be

As a rule, Excel automatically sets page breaks for printing based on paper size, scale, specified number of sheets in width and height, page orientation, and other parameters. You can use VPageBreak object and HPageBreak object in combination with a number of .PageSetup properties to manually fit page breaks. Note that you cannot set page breaks with the .PrintArea property (see below in the code for why). In the following code I set page breaks after the cells "AF1", "BE1", "CD1", "DA1", "DX1":
Sub print_area()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Worksheets
With ws.PageSetup
.PrintTitleColumns = "$A:$E"
.PrintArea = "$F$1:$AF$125,$AG$1:$BE$125,$BF$1:$CD$125,$CE$1:$DA$125,$DB$1:$DX$125" ' there will be one area between the upper left and lower right cells
Debug.Print .PrintArea ' check the final .PrintArea; prints $F$1:$DX$125
.Zoom = False
.Orientation = xlLandscape
.FitToPagesWide = False 'it's Auto
.FitToPagesTall = 1
.CenterHorizontally = True
ws.ResetAllPageBreaks
breaks = Array("AF1", "BE1", "CD1", "DA1", "DX1") 'zero-based array
For i = 1 To UBound(breaks)
ws.VPageBreaks.Add Before:=ws.Range(breaks(i - 1)).Offset(, 1)
Next
End With
Next
End Sub
Please note that in response to your pagination actions, Excel can
(and usually does) change the pagination itself

Related

How to set PrintOut method to a single a4 page

I have the code below, im trying to make my selection of cells print on one a4 page rather than spread across 6 in a command button that is on a sheet, can anyone point out what im doing wrong? I expected to be able to have a printing window like with saving a sheet or something like that, but this is all i can find. thanks a tonne for your help :)
Private Sub CommandButton3_Click()
Sheets("Home").PageSetup.PrintArea = "$A$1:$W$44"
Sheets("Home").PageSetup.Orientation = xlLandscape
Sheets("Home").PageSetup.FitToPagesWide = 1
Sheets("Home").PageSetup.FitToPagesTall = 1
Sheets("Home").PrintPreview (EnableChanges = True)
'Sheets("Home").PrintOut (Preview = True)
End Sub
Ive looked for other some VBA online and looked up the pagesetup, but i cant seem to find what im after, the code above is the closest ive found, but it makes no difference to the printing process.
Is this what you are trying? I have commented the relevant parts of the code. Let me know if you still have any questions.
Option Explicit
Sub Sample()
With ThisWorkbook.Sheets("Home")
With .PageSetup
.PrintArea = "$A$1:$W$44"
.Orientation = xlLandscape
.FitToPagesWide = 1
.FitToPagesTall = 1
End With
'~~> To fit text spanning in different pages change the view
ActiveWindow.View = xlPageBreakPreview
'~~> Extend the Horizontal Pagebreak to extreme right
'~~> This mimics what you do when you are dragging the PgBrk
If .HPageBreaks.Count > 0 Then _
.HPageBreaks(1).DragOff Direction:=xlDown, RegionIndex:=1
'~~> Similarly extend the Vertical Pagebreak to extreme down
If .VPageBreaks.Count > 0 Then _
.VPageBreaks(1).DragOff Direction:=xlToRight, RegionIndex:=1
.PrintPreview EnableChanges:=True
'~~> Change the view back to normal
ActiveWindow.View = xlNormalView
End With
End Sub
BEFORE
and the print preview
AFTER

Adjusting display depending on users screen resolution?

Im working on a project for a VBA program for which the the entire program is to be viewed in maximized/fullscreen view. No Tabs, Formula bars etc. The user will navigate the worksheets with custom buttons and commands. The issue im having is that most of my design is coming from my laptop and (with res 1366/768) and when it is viewed on a bigger monitor (res 2880/1620) the view is not aesthetically pleasing.
Is there a way to code VBA to adjust the view based on the users screen size? Im aware of using range.zoom but that will just cause the images to be absurdly massive on a larger screen even though it would be to scale it would be more pleasing to the eye for the zoom to be set back when the screen gets to a certain size. Even if the process coudn't be automatic. im not opposed an input box opening at the start of the program for the user to select small, medium or large before it runs.
Private Sub Workbook_Open()
Dim wsh As Worksheet
Dim CarryOn As Integer
Set wbPB = PokerBros
Set wsh = wbPB.Worksheets("Home")
CarryOn = MsgBox("Do you want to save a copy of this original file?", vbQuestion + vbYesNo, "Save Copy Recommended")
If CarryOn = vbYes Then
Call CopyToNewBook
End If
wsh.Activate
Call ZoomToFitHome
Call ScreenDisplayMax
End Sub
Sub ZoomToFitHome()
Dim wbh As Worksheet
Set wbPB = PokerBros
Set wbh = wbPB.Worksheets("Home")
wbh.Range("A1:AA30").Select
ActiveWindow.Zoom = True 'set range zoom
End Sub
Sub ScreenDisplayMax()
' Call UnProtectAllSheets
With Application
.DisplayStatusBar = False
.DisplayFullScreen = True
With ActiveWindow
.WindowState = xlMaximized
.DisplayHeadings = False
.DisplayWorkbookTabs = False
.DisplayGridlines = False
.DisplayHorizontalScrollBar = True
.DisplayVerticalScrollBar = True
End With
.DisplayFormulaBar = False
End With
End Sub

VBA - Set multiple filters at once in a pivot table without updating in between

I have a script that automates setting some filters on a pivot table depending on the data (hiding and showing certain articles). Now the script works great but when I have a pivot chart it gets super slow. I think what's happening in the background is that the chart is re-calculated for every article I show/hide.
Is there a way to set multiple filters without updating the table and then update the pivot chart & table once they are all set?
I thought about deleting the chart, setting the filters and then re-creating the chart. But this would remove any formatting I set on the chart so I would prefer not to go that path.
Sub btnVar() ' function to show only articles with variable data
onlyVar (True)
End Sub
Sub btnConst() ' function to show only articles with constant data
onlyVar (False)
End Sub
Sub onlyVar(check As Boolean)
calcOff 'disable screen updates etc
'first show all articles
ActiveSheet.PivotTables("PivotTable1").PivotFields("Artikel").ClearAllFilters
Dim dict As Dictionary
Set dict = New Dictionary
art = 9 'start from column 9
article = Sheet4.Cells(3, art).Value 'first article name
lrow = Cells(Rows.Count, art).End(xlUp).Row 'find last row
' set article visibility in dictionary according to desired filter (constant/variable
Do While article <> "" 'loop over article names until end of table
If Sheet4.Cells(4, art).Value = Application.Average(Range(Cells(4, art), Cells(lrow, art))) Then
dict.Add article, Not check
Else
dict.Add article, check
End If
art = art + 1
article = Sheet4.Cells(3, art).Value
Loop
' set the filters, this causes the table & chart to update every time a filter is set.
' it is fine for table only but with chart it gets super slow.
With ActiveSheet.PivotTables("PivotTable1").PivotFields("Artikel")
For Each ptitm In .PivotItems
ptitm.Visible = dict.Item(ptitm.Name)
Next
End With
calcOn 're-enable screen updates etc
End Sub
Public Sub calcOff()
Application.Calculation = xlCalculationManual
Application.DisplayStatusBar = False
Application.EnableEvents = False
Application.ScreenUpdating = False
End Sub
Public Sub calcOn()
Application.Calculation = xlCalculationAutomatic
Application.DisplayStatusBar = True
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Any help to make this faster would be appreciated. Thanks in advance!

Custom printing area macro

I'm making a macro that sets a print area to user selected areas of document. Basically there is a box next to a bunch of cells and if user ticks the box then the bunch of cells is included to the print area.
Here is my code so far:
Sub TestCellA1()
Dim t As Integer, d As Integer
t = 0
d = 20
Dim rng_per As Range
Set rng_per = Range("A3:E328") 'prints whole document
Dim rng1 As Range
If Not IsEmpty(Range("F19")) = True Then
ActiveSheet.PageSetup.PrintArea = Range(rng_per)
Else
Do While t < 10
If IsEmpty(Range("F" & d).Value) = True Then
'MsgBox "Do not print"
Else
'MsgBox "Do print"
ActiveSheet.PageSetup.PrintArea = rng1
End If
t = t + 1
d = d + 25
Loop
End If
End Sub
So far this works to the point where the actual work is supposed to be done. I planned that everytime when loop finds box ticked it adds that part of document to the print area. As a newbie with vba I have no idea how to add those areas to print area. Any ideas how to do it? Thanks in advance& have a good day.
If you create and load a range into rng_to_add, the following will take the existing PrintArea and Union (append to) the rng_to_add:
' Going into this, you need to have declared a variable called rng_to_add
Dim rng_to_add As Range
' and loaded the range area you want to add to the PrintArea. This will
' be different for your particular situation.
Set rng_to_add = Sheets("Sheet1").Range("A1:C3")
' Referring to the current PageSetup of the Activesheet..
With ActiveSheet.PageSetup
' Check if the PrintArea of above PageSetup is empty
If .PrintArea = "" Then
' If so, set the PrintArea to the address of the Range: rng_to_add
.PrintArea = rng_to_add.Address
Else
' If not, set it to the address of a union (append) of the existing
' PrintArea's range and the address of the Range: rng_to_add
.PrintArea = Union(Range(.PrintArea), rng_to_add).Address
End If
' End the reference to the current PageSetup of the Activesheet
End With
So, for portability and/or integrating into your existing routines, you could create subroutines that manage the PrintArea like so:
Sub Clear_PrintArea()
' Set PrintArea to nothing
ActiveSheet.PageSetup.PrintArea = ""
End Sub
Sub Add_range_to_PrintArea(rng_to_add As Range)
' Referring to the current PageSetup of the Activesheet..
With ActiveSheet.PageSetup
' Check if the PrintArea of above PageSetup is empty
If .PrintArea = "" Then
' If so, set the PrintArea to the address of the Range: rng_to_add
.PrintArea = rng_to_add.Address
Else
' If not, set it to the address of a union (append) of the existing
' PrintArea's range and the address of the Range: rng_to_add
.PrintArea = Union(Range(.PrintArea), rng_to_add).Address
End If
' End the reference to the current PageSetup of the Activesheet
End With
End Sub
You could then call it like so:
Clear_PrintArea
Add_range_to_PrintArea Range("A1:C3")
Add_range_to_PrintArea Range("A7:C10")
Add_range_to_PrintArea Range("A13:C16")

excel vba freeze pane without select

I have a VBA script in Excel that freezes the panes of an Excel worksheet, but I'm curious to see if this is possible without first selecting a range. This is my current code which freezes rows 1–7 but uses Range.Select:
ActiveSheet.Range("A8").Select
ActiveWindow.FreezePanes = True
Any suggestions?
Record yourself using the View ► Freeze Panes ► Freeze Top Row command and this is what you get for .FreezePanes.
With ActiveWindow
If .FreezePanes Then .FreezePanes = False
.SplitColumn = 0
.SplitRow = 1
.FreezePanes = True
End With
So modifying the .SplitColumn and/or .SplitRow properties should do it for you regardless on what the ActiveCell property is.
There are many things to get wrong about freezing panes. I add my own answer, so I will find it here, and won't have to reinvent it next time.
Public Sub FreezePanesAt(rngDataTopLeft As Range)
Dim wndCurrent As Window
For Each wndCurrent In rngDataTopLeft.Worksheet.Parent.Windows
With wndCurrent
.FreezePanes = False
If Not ((rngDataTopLeft.Row = 1) And (rngDataTopLeft.Column = 1)) Then
.ScrollRow = 1
.ScrollColumn = 1
.SplitRow = rngDataTopLeft.Row - 1
.SplitColumn = rngDataTopLeft.Column - 1
.FreezePanes = True
End If
End With
Next
End Sub
Example usage:
FreezePanesAt ThisWorkbook.Worksheets("Sheet1").Range("B3")
FreezePanesAt ThisWorkbook.Names("Header").RefersToRange
The input parameter is the top left cell of the bottom right pane; I think this is the most frequent use case: you know the range at which to split and don't care about which workbook / worksheet / window it is in
If the input parameter is in the first row / first cell but not A1, then there will be only two panes; A1 is a special case, however, Excel would split the window at center of the current view, I prevented this because I can't think of any case where this would be intended
It iterates through all Windows attached to the workbook / worksheet; indexing into Application.Windows (Windows(Thisworkbook.Name)) won't cause an error if you have more windows to the same workbook (the name would be "MyWorkbook:1"), or Excel attempted (which usually fails) to repair a workbook after a crash (the name would be "MyWorkbook [Repaired]")
It takes into consideration that panes may already be frozen and the user / another macro might have scrolled to a location in the workbook, and the top left cell in the window is not A1
I found the previous answers only worked with some sheets when looping through tabs. I found the following code worked on every tab I looped through (target was a single workbook), despite which workbook was the activeworkbook.
The short of it:
With Application.Windows(DataWKB.Name)
Application.Goto ws.Cells(4, 5)
.SplitColumn = 4
.SplitRow = 3
.FreezePanes = True
End With
The code as it is in my Sub: (be aware, I do a lot more formatting in this sub, I tried to strip that out and leave just the code needed here)
Sub Format_Final_Report()
Dim DataWKB As Workbook
Set DataWKB = Workbooks("Report.xlsx")
Dim ws As Worksheet
Dim tabCNT As Long
Dim tabName As String
tabCNT = DataWKB.Sheets.Count
For i = 1 To tabCNT
Set ws = DataWKB.Worksheets(i)
tabName = ws.Name
With Application.Windows(DataWKB.Name)
Application.Goto ws.Cells(4, 5)
.SplitColumn = 4
.SplitRow = 3
.FreezePanes = True
End With
Next i
End Sub
Hopefully, this will save someone some research time in the future.
I need to be able to properly refreeze panes (when creating new windows, notably) without losing the activecell or messing up the visible range. It took a lot of playing around but I think I have something solid that works:
Sub FreezePanes(nbLignes As Integer, nbColonnes As Integer, Optional ByVal feuille As Worksheet)
If feuille Is Nothing Then Set feuille = ActiveSheet Else feuille.Activate
Error GoTo erreur
With ActiveWindow
If .View = xlNormalView Then
If .FreezePanes Then .FreezePanes = False
If .Split Then .Split = False
.SplitColumn = nbColonnes
.SplitRow = nbLignes
If .Panes.Count = 4 Then 'rows and columns frozen
.Panes(1).ScrollRow = 1
.Panes(1).ScrollColumn = 1
.Panes(2).ScrollRow = 1 'top right pane
.Panes(3).ScrollColumn = 1 'bottom left pane
ElseIf nbLignes > 0 Then .Panes(1).ScrollRow = 1
ElseIf nbColonnes > 0 Then .Panes(1).ScrollColumn = 1
Else: GoTo erreur
End If
.FreezePanes = True
End If
End With
Exit Sub
erreur:
Debug.print "Erreur en exécutant le sub 'FreezePanes " & nbLignes & ", " & nbColonnes & ", '" & feuille.Name & "' : code #" & Err.Number & Err.Description
End Sub
I know this is old but I came across this tidbit that may be useful...
as ChrisB stated, the SplitColumn/SplitRow values represent the last cell above/left of the split BUT of the currently visible window. So if you happen to have code like this:
Application.Goto Worksheets(2).Range("A101"), True
With ActiveWindow
.SplitColumn = 0
.SplitRow = 10
.FreezePanes = True
End With
The split will be between rows 110 and 111 instead of 10 and 11.
edited for clarification and to add more information:
My point is that the values are offsets of the upper left cell, not an address of a cell. Therefore, ChrisB's Dec 4 '15 at 18:34 comment under the main answer only holds if row 1 is visible in the Activewindow.
A couple of other points on this:
using Application.goto doesn't necessarily put whichever cell you
are trying to go to in the upper left
the cell that is put in the upper left when using .goto can depend
on the size of the excel window, the current zoom level, etc (so fairly arbitrary)
it is possible to have the splits placed so that you can not see
them or even scroll around in the visible window (if .FreezePanes =
true). for example:
Application.Goto Worksheets(1).Range("A1"), True
With ActiveWindow
.SplitColumn = 100
.SplitRow = 100
.FreezePanes = True
End With
CETAB may be dealing with this in their answer.
Yes, the ActiveWindow.ScrollRow = 1 and ActivWindow.ScrollColumn = 1 is a must for FreezePanes if your visible window does not include cell A1.
If you are freezing rows 1:3 by selecting row 4 or cell A4, and cell A3 is not visible, the FreezePanes function will freeze the window in the center of the visible window.
Also if cell B4 is selected, and column A is not visible, then only the rows 1:3 will be frozen (column A will not frozen). Similarly, if rows 1:3 are not visible, only column A will be frozen. If both column A and rows 1:3 are not visible, the FreezePanes function will freeze the window in the center of the visible window.
The problem with splitting is that if a user unfreezes panes, the panes will remain split. (I couldn't find a way to turn off split afterwards while keeping the panes frozen)
This may be too obvious/simple, but what if the current selection is simply saved and then re-selected afterwards?
Sub FreezeTopRow()
'First save the current selection to go back to it later
Dim rngOriginalSelection As Range
Set rngOriginalSelection = Selection
'Change selection to A2 to make .FreezePanes work
ActiveSheet.Range("A2").Select
ActiveWindow.FreezePanes = True
'Change selection back to original
rngOriginalSelection.Select
End Sub
Here is what i use...
Public Sub FreezeTopRowPane(ByRef MyWs As Excel.Worksheet, _
Optional ByVal AfterRowNr As Integer = 1)
Dim SavedWS As Worksheet
Dim SavedUpdating As Boolean
SavedUpdating = Application.ScreenUpdating 'save current screen updating mode
Set SavedWS = ActiveSheet 'save current active sheet
Application.ScreenUpdating = False 'turn off screen updating
MyWs.Activate 'activate worksheet for panes freezing
ActiveWindow.FreezePanes = False 'turn off freeze panes in case
With ActiveWindow
.SplitColumn = 0 'set no column to split
.SplitRow = AfterRowNr 'set the row to split, default = row 1
End With
ActiveWindow.FreezePanes = True 'trigger the new pane freezing
SavedWS.Activate 'restore previous (saved) ws as active
Application.ScreenUpdating = SavedUpdating 'restore previous (saved) updating mode
End Sub
I did a timing test of Freezing using .Select vs .Activate. Here is the code
Dim numLoops As Long
Dim StartTime, LoopTime As Long
numLoops = 1000
Debug.Print ("Timing test of numloops:" & numLoops)
StartTime = Timer
For I = 0 To numLoops
targetSheet.Activate
With ActiveWindow
If .FreezePanes Then .FreezePanes = False
.SplitColumn = 2
.SplitRow = 1
.FreezePanes = True
End With
Next I
LoopTime = Timer
Debug.Print ("Total time of activate method:" & Format((LoopTime - StartTime) / 86400, "hh:mm:ss"))
StartTime = Timer
For I = 0 To numLoops
targetSheet.Select
Application.Range("C2").Select
Application.ActiveWindow.FreezePanes = True
Next I
LoopTime = Timer
Debug.Print ("Total time of select method:" & Format((LoopTime - StartTime) / 86400, "hh:mm:ss"))
And here are the results.
Timing test of numloops:1000
Total time of activate method:00:00:39
Total time of select method:00:00:01
As you can see, .Select is much faster.

Resources