Changes to Export-Excel & New-PivotTableDefinition

Export-Excel.ps1 
* Added MoveToStart, MoveBefore and MoveAfter Parameters 
* Added support for URI item properties. If a property is of type URI it is created as a hyperlink
* Fixed issue where AutoNamedRange, NamedRange, and TableName do not work when appending to a sheet which already contains the range(s) / table  
* Fixed issues when pivottables / charts already exist and an export tries to create them again.   
* Allowed PivotFilter and PivotDataToColumn, ChartHeight/width ChartRow/Column, ChartRow/ColumnPixelOffsets,  to be passed in -PivotTableDefinition and specified when running New-PivotDefinition. 
* Bad column-names specified for Pivots now generate warnings instead of throwing.
* Removed the need to specify a fill type when specifying a title background color
* "flattened out" small "called-once" function , add-title, convert-toNumber and Stop-ExcelProcess.
This commit is contained in:
jhoneill
2018-06-15 10:23:06 +01:00
committed by GitHub
parent e47e1d99c1
commit 520bb079e5

View File

@@ -1,4 +1,4 @@
Function Export-Excel { function Export-Excel {
<# <#
.SYNOPSIS .SYNOPSIS
Export data to an Excel worksheet. Export data to an Excel worksheet.
@@ -26,7 +26,9 @@
.PARAMETER TitleSize .PARAMETER TitleSize
Sets the point size for the title Sets the point size for the title
.PARAMETER TitleBackgroundColor .PARAMETER TitleBackgroundColor
Sets the cell background to solid and the chose colour for the title cell Sets the cell background color for the title cell
.PARAMETER TitleFillPattern
Sets the fill pattern for the title cell
.PARAMETER Password .PARAMETER Password
Sets password protection on the workbook Sets password protection on the workbook
.PARAMETER IncludePivotTable .PARAMETER IncludePivotTable
@@ -52,7 +54,7 @@
.PARAMETER NoNumberConversion .PARAMETER NoNumberConversion
By default we convert all values to numbers if possible, but this isn't always desirable. NoNumberConversion allows you to add exceptions for the conversion. Wildcards (like '*') are allowed. By default we convert all values to numbers if possible, but this isn't always desirable. NoNumberConversion allows you to add exceptions for the conversion. Wildcards (like '*') are allowed.
.PARAMETER BoldTopRow .PARAMETER BoldTopRow
Makes the top Row boldface. Makes the top Row boldface
.PARAMETER NoHeader .PARAMETER NoHeader
Does not put field names at the top of columns Does not put field names at the top of columns
.PARAMETER RangeName .PARAMETER RangeName
@@ -65,6 +67,18 @@
A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts
.PARAMETER HideSheet .PARAMETER HideSheet
Name(s) of Sheet(s) to hide in the workbook Name(s) of Sheet(s) to hide in the workbook
.PARAMETER MoveToStart
If specified, the worksheet will be moved to the start of the workbook.
MoveToStart takes precedence over MoveToEnd, Movebefore and MoveAfter if more than one is specified
.PARAMETER MoveToEnd
If specified, the worksheet will be moved to the end of the workbook.
(This is the default position for newly created sheets, but this can be used to move existing sheets)
.PARAMETER MoveBefore
If specified, the worksheet will be moved before the nominated one (which can be a postion starting from 1, or a name).
MoveBefore takes precedence over MoveAfter if both are specified
.PARAMETER MoveAfter
If specified, the worksheet will be moved after the nominated one (which can be a postion starting from 1, or a name or *).
If * is used, the worksheet names will be examined starting with the first one sheet placed after the last sheet which comes before it alphabetically.
.PARAMETER KillExcel .PARAMETER KillExcel
Closes Excel - prevents errors writing to the file because Excel has it open Closes Excel - prevents errors writing to the file because Excel has it open
.PARAMETER AutoNameRange .PARAMETER AutoNameRange
@@ -73,7 +87,6 @@
Row to start adding data. 1 by default. Row 1 will contain the title if any. Then headers will appear (Unless -No header is specified) then the data appears Row to start adding data. 1 by default. Row 1 will contain the title if any. Then headers will appear (Unless -No header is specified) then the data appears
.PARAMETER StartColumn .PARAMETER StartColumn
Column to start adding data - 1 by default Column to start adding data - 1 by default
.PARAMETER FreezeTopRow .PARAMETER FreezeTopRow
Freezes headers etc. in the top row Freezes headers etc. in the top row
.PARAMETER FreezeFirstColumn .PARAMETER FreezeFirstColumn
@@ -84,13 +97,10 @@
Freezes panes at specified coordinates (in the form RowNumber , ColumnNumber) Freezes panes at specified coordinates (in the form RowNumber , ColumnNumber)
.PARAMETER AutoFilter .PARAMETER AutoFilter
Enables the 'Filter' in Excel on the complete header row. So users can easily sort, filter and/or search the data in the select column from within Excel. Enables the 'Filter' in Excel on the complete header row. So users can easily sort, filter and/or search the data in the select column from within Excel.
.PARAMETER AutoSize .PARAMETER AutoSize
Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell. Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell.
.PARAMETER Now .PARAMETER Now
The 'Now' switch is a shortcut that creates automatically a temporary file, enables 'AutoSize', 'AutoFiler' and 'Show', and opens the file immediately. The 'Now' switch is a shortcut that creates automatically a temporary file, enables 'AutoSize', 'AutoFiler' and 'Show', and opens the file immediately.
.PARAMETER NumberFormat .PARAMETER NumberFormat
Formats all values that can be converted to a number to the format specified. Formats all values that can be converted to a number to the format specified.
@@ -329,8 +339,9 @@
[OfficeOpenXml.ExcelPackage]$ExcelPackage, [OfficeOpenXml.ExcelPackage]$ExcelPackage,
[Parameter(ValueFromPipeline = $true)] [Parameter(ValueFromPipeline = $true)]
$TargetData, $TargetData,
[String]$Password, [Switch]$Show,
[String]$WorkSheetname = 'Sheet1', [String]$WorkSheetname = 'Sheet1',
[String]$Password,
[switch]$ClearSheet, [switch]$ClearSheet,
[switch]$Append, [switch]$Append,
[String]$Title, [String]$Title,
@@ -351,7 +362,6 @@
[Switch]$ShowCategory, [Switch]$ShowCategory,
[Switch]$ShowPercent, [Switch]$ShowPercent,
[Switch]$AutoSize, [Switch]$AutoSize,
[Switch]$Show,
[Switch]$NoClobber, [Switch]$NoClobber,
[Switch]$FreezeTopRow, [Switch]$FreezeTopRow,
[Switch]$FreezeFirstColumn, [Switch]$FreezeFirstColumn,
@@ -385,6 +395,10 @@
[OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6', [OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6',
[Object[]]$ExcelChartDefinition, [Object[]]$ExcelChartDefinition,
[String[]]$HideSheet, [String[]]$HideSheet,
[Switch]$MoveToStart,
[Switch]$MoveToEnd,
$MoveBefore ,
$MoveAfter ,
[Switch]$KillExcel, [Switch]$KillExcel,
[Switch]$AutoNameRange, [Switch]$AutoNameRange,
[Int]$StartRow = 1, [Int]$StartRow = 1,
@@ -405,15 +419,7 @@
) )
Begin { Begin {
function Find-WorkSheet { function Add-CellValue {
param (
$WorkSheetName
)
$pkg.Workbook.Worksheets | Where-Object {$_.name -match $WorkSheetName}
}
Function Add-CellValue {
<# <#
.SYNOPSIS .SYNOPSIS
Save a value in an Excel cell. Save a value in an Excel cell.
@@ -440,7 +446,16 @@
break break
#endregion #endregion
} }
{ $_ -is [URI] } {
#region Save a hyperlink
$TargetCell.Value = $_.AbsoluteUri
$TargetCell.HyperLink = $_
$TargetCell.Style.Font.Color.SetColor([System.Drawing.Color]::Blue)
$TargetCell.Style.Font.UnderLine = $true
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($_.AbsoluteUri)' as Hyperlink"
break
#endregion
}
{ $_ -is [DateTime]} { { $_ -is [DateTime]} {
#region Save a date with an international valid format #region Save a date with an international valid format
$TargetCell.Value = $_ $TargetCell.Value = $_
@@ -452,7 +467,7 @@
{(($NoNumberConversion) -and ($NoNumberConversion -contains $Name)) -or {(($NoNumberConversion) -and ($NoNumberConversion -contains $Name)) -or
($NoNumberConversion -eq '*')} { ($NoNumberConversion -eq '*')} {
#regioon Save a value without converting to number #region Save a value without converting to number
$TargetCell.Value = $_ $TargetCell.Value = $_
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' unconverted" Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' unconverted"
break break
@@ -461,8 +476,10 @@
Default { Default {
#region Save a value as a number if possible #region Save a value as a number if possible
if (($Number = ConvertTo-Number $_) -ne $null) { $number = $null
$TargetCell.Value = $Number if ([Double]::TryParse([String]$_, [System.Globalization.NumberStyles]::Any,
[System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number)) {
$TargetCell.Value = $number
$targetCell.Style.Numberformat.Format = $Numberformat $targetCell.Style.Numberformat.Format = $Numberformat
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' as number converted from '$_' with format '$Numberformat'" Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$($TargetCell.Value)' as number converted from '$_' with format '$Numberformat'"
} }
@@ -476,63 +493,12 @@
} }
} }
Function Add-Title {
<#
.SYNOPSIS
Add a title row to the Excel worksheet.
#>
$ws.Cells[$Row, $StartColumn].Value = $Title
$ws.Cells[$Row, $StartColumn].Style.Font.Size = $TitleSize
if ($TitleBold) {
#set title to Bold if -TitleBold was specified.
#Otherwise the default will be unbolded.
$ws.Cells[$Row, $StartColumn].Style.Font.Bold = $True
}
$ws.Cells[$Row, $StartColumn].Style.Fill.PatternType = $TitleFillPattern
#can only set TitleBackgroundColor if TitleFillPattern is something other than None
if ($TitleBackgroundColor -AND ($TitleFillPattern -ne 'None')) {
$ws.Cells[$Row, $StartColumn].Style.Fill.BackgroundColor.SetColor($TitleBackgroundColor)
}
elseif ($TitleBackgroundColor) {
Write-Warning "Title Background Color ignored. You must set the TitleFillPattern parameter to a value other than 'None'. Try 'Solid'."
}
}
Function ConvertTo-Number {
<#
.SYNOPSIS
Convert a value to a number
#>
Param (
[String]$Value
)
$R = $null
if ([Double]::TryParse([String]$Value, [System.Globalization.NumberStyles]::Any,
[System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$R)) {
$R
}
}
Function Stop-ExcelProcess {
<#
.SYNOPSIS
Stop the Excel process when it's running.
#>
Get-Process excel -ErrorAction Ignore | Stop-Process
while (Get-Process excel -ErrorAction Ignore) {}
}
Try { Try {
$script:Header = $null $script:Header = $null
if ($append -and $clearSheet) {throw "You can't use -Append AND -ClearSheet."} if ($append -and $clearSheet) {throw "You can't use -Append AND -ClearSheet."}
if ($KillExcel) { if ($KillExcel) {
Stop-ExcelProcess Get-Process excel -ErrorAction Ignore | Stop-Process
while (Get-Process excel -ErrorAction Ignore) {}
} }
if ($PSBoundParameters.Keys.Count -eq 0 -Or $Now) { if ($PSBoundParameters.Keys.Count -eq 0 -Or $Now) {
@@ -564,36 +530,81 @@
} }
[OfficeOpenXml.ExcelWorksheet]$ws = $pkg | Add-WorkSheet -WorkSheetname $WorkSheetname -NoClobber:$NoClobber -ClearSheet:$ClearSheet #Add worksheet doesn't take any action for -noClobber [OfficeOpenXml.ExcelWorksheet]$ws = $pkg | Add-WorkSheet -WorkSheetname $WorkSheetname -NoClobber:$NoClobber -ClearSheet:$ClearSheet #Add worksheet doesn't take any action for -noClobber
if ($MoveToStart) {$pkg.Workbook.Worksheets.MoveToStart($worksheetName) }
elseif ($MoveToEnd) {$pkg.Workbook.Worksheets.MoveToEnd($worksheetName) }
elseif ($MoveBefore) {
if ($pkg.Workbook.Worksheets[$MoveBefore]) {
if ($MoveBefore -is [int]) {
$pkg.Workbook.Worksheets.MoveBefore($ws.Index, $MoveBefore)
}
else {$pkg.Workbook.Worksheets.MoveBefore($worksheetname, $MoveBefore)}
}
else {Write-Warning "Can't find worksheet '$MoveBefore'; worsheet '$WorkSheetname' will not be moved."}
}
elseif ($MoveAfter) {
if ($MoveAfter = "*") {
if ($WorkSheetname -lt $pkg.Workbook.Worksheets[1].Name) {$pkg.Workbook.Worksheets.MoveToStart($worksheetName)}
else {
$i = 1
While ($i -lt $pkg.Workbook.Worksheets.Count -and $pkg.Workbook.Worksheets[$i + 1].Name -lt $worksheetname ) { $i++}
$pkg.Workbook.Worksheets.MoveAfter($ws.Index, $i)
}
}
elseif ($pkg.Workbook.Worksheets[$MoveAfter]) {
if ($MoveAfter -is [int]) {
$pkg.Workbook.Worksheets.MoveAfter($ws.Index, $MoveAfter)
}
else {
$pkg.Workbook.Worksheets.MoveAfter($worksheetname, $MoveAfter)
}
}
else {Write-Warning "Can't find worksheet '$MoveAfter'; worsheet '$WorkSheetname' will not be moved."}
}
$ws.View.TabSelected = $true
foreach ($format in $ConditionalFormat ) { foreach ($format in $ConditionalFormat ) {
$target = "Add$($format.Formatter)" $target = "Add$($format.Formatter)"
$rule = ($ws.ConditionalFormatting).PSObject.Methods[$target].Invoke($format.Range, $format.IconType) $rule = ($ws.ConditionalFormatting).PSObject.Methods[$target].Invoke($format.Range, $format.IconType)
$rule.Reverse = $format.Reverse $rule.Reverse = $format.Reverse
} }
if ($append) { if ($append -and $ws.Dimension) {
$headerRange = $ws.Dimension.Address -replace "\d+$", "1" $headerRange = $ws.Dimension.Address -replace "\d+$", "1"
#if there is a title or anything else above the header row, specifying StartRow will skip it. #if there is a title or anything else above the header row, specifying StartRow will skip it.
if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1", "$StartRow"} if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1", "$StartRow"}
#$script:Header = $ws.Cells[$headerrange].Value #$script:Header = $ws.Cells[$headerrange].Value
#using a slightly odd syntax otherwise header ends up as a 2D array #using a slightly odd syntax otherwise header ends up as a 2D array
$ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ } $ws.Cells[$headerRange].Value | ForEach-Object -Begin {$Script:header = @()} -Process {$Script:header += $_ }
$row = $ws.Dimension.Rows $row = $ws.Dimension.Rows
Write-Debug -Message ("Appending: headers are " + ($script:Header -join ", ") + "Start row $row") Write-Debug -Message ("Appending: headers are " + ($script:Header -join ", ") + "Start row $row")
} }
elseif ($Title) { elseif ($Title) {
#Can only add a title if not appending #Can only add a title if not appending
$Row = $StartRow $Row = $StartRow
Add-Title $ws.Cells[$Row, $StartColumn].Value = $Title
$ws.Cells[$Row, $StartColumn].Style.Font.Size = $TitleSize
if ($TitleBold) {
#set title to Bold if -TitleBold was specified.
#Otherwise the default will be unbolded.
$ws.Cells[$Row, $StartColumn].Style.Font.Bold = $True
}
#can only set TitleBackgroundColor if TitleFillPattern is something other than None
if ($TitleBackgroundColor -and ($TitleFillPattern -eq 'None')) {
$TitleFillPattern = [OfficeOpenXml.Style.ExcelFillStyle]::Solid
}
$ws.Cells[$Row, $StartColumn].Style.Fill.PatternType = $TitleFillPattern
if ($TitleBackgroundColor ) {
$ws.Cells[$Row, $StartColumn].Style.Fill.BackgroundColor.SetColor($TitleBackgroundColor)
}
$Row ++ ; $startRow ++ $Row ++ ; $startRow ++
} }
else { else {
$Row = $StartRow $Row = $StartRow
} }
$ColumnIndex = $StartColumn $ColumnIndex = $StartColumn
$firstTimeThru = $true $firstTimeThru = $true
$isDataTypeValueType = $false $isDataTypeValueType = $false
$pattern = 'string|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort'
} }
Catch { Catch {
if ($AlreadyExists) { if ($AlreadyExists) {
@@ -611,7 +622,7 @@
Try { Try {
if ($firstTimeThru) { if ($firstTimeThru) {
$firstTimeThru = $false $firstTimeThru = $false
$isDataTypeValueType = $TargetData.GetType().name -match $pattern $isDataTypeValueType = $TargetData.GetType().name -match 'string|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort'
Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'" Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'"
} }
@@ -620,7 +631,6 @@
Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData Add-CellValue -TargetCell $ws.Cells[$Row, $ColumnIndex] -CellValue $TargetData
$ColumnIndex += 1
$Row += 1 $Row += 1
} }
else { else {
@@ -669,7 +679,7 @@
#if there is a title or anything else above the header row, specifying StartRow will skip it. #if there is a title or anything else above the header row, specifying StartRow will skip it.
if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1", "$StartRow"} if ($StartRow -ne 1) {$headerRange = $headerRange -replace "1", "$StartRow"}
#using a slightly odd syntax otherwise header ends up as a 2D array #using a slightly odd syntax otherwise header ends up as a 2D array
$ws.Cells[$headerRange].Value | foreach -Begin {$Script:header = @()} -Process {$Script:header += $_ } $ws.Cells[$headerRange].Value | ForEach-Object -Begin {$Script:header = @()} -Process {$Script:header += $_ }
} }
$totalRows = $ws.Dimension.End.Row $totalRows = $ws.Dimension.End.Row
$totalColumns = $ws.Dimension.Columns $totalColumns = $ws.Dimension.Columns
@@ -677,7 +687,8 @@
$targetRangeName = "$($script:Header[$c])" $targetRangeName = "$($script:Header[$c])"
$targetColumn = $c + $StartColumn $targetColumn = $c + $StartColumn
$theCell = $ws.Cells[($startrow + 1), $targetColumn, $totalRows , $targetColumn ] $theCell = $ws.Cells[($startrow + 1), $targetColumn, $totalRows , $targetColumn ]
$ws.Names.Add($targetRangeName, $theCell) | Out-Null if ($ws.names[$targetRangeName]) { $ws.names[$targetRangeName].Address = $theCell.FullAddressAbsolute }
else {$ws.Names.Add($targetRangeName, $theCell) | Out-Null }
if ([OfficeOpenXml.FormulaParsing.ExcelUtilities.ExcelAddressUtil]::IsValidAddress($targetRangeName)) { if ([OfficeOpenXml.FormulaParsing.ExcelUtilities.ExcelAddressUtil]::IsValidAddress($targetRangeName)) {
Write-Warning "AutoNameRange: Property name '$targetRangeName' is also a valid Excel address and may cause issues. Consider renaming the property name." Write-Warning "AutoNameRange: Property name '$targetRangeName' is also a valid Excel address and may cause issues. Consider renaming the property name."
@@ -697,7 +708,8 @@
Write-Debug "Data Range '$dataRange'" Write-Debug "Data Range '$dataRange'"
if (-not [String]::IsNullOrEmpty($RangeName)) { if (-not [String]::IsNullOrEmpty($RangeName)) {
$ws.Names.Add($RangeName, $ws.Cells[$dataRange]) | Out-Null if ($ws.Names[$RangeName]) { $ws.Names[$rangename].Address = $ws.Cells[$dataRange].FullAddressAbsolute }
else {$ws.Names.Add($RangeName, $ws.Cells[$dataRange]) | Out-Null }
} }
if (-not [String]::IsNullOrEmpty($TableName)) { if (-not [String]::IsNullOrEmpty($TableName)) {
@@ -708,163 +720,175 @@
$cec = $ws.Dimension.End.Column # was $script:Header.Count $cec = $ws.Dimension.End.Column # was $script:Header.Count
$targetRange = $ws.Cells[$csr, $csc, $cer, $cec] $targetRange = $ws.Cells[$csr, $csc, $cer, $cec]
#if we're appending data the table may already exist: but excel doesn't like the result if I put #if we're appending data the table may already exist:
# if ($ws.Tables[$TableName]) {$ws.Tables.Delete($TableName) } if ($ws.Tables[$TableName]) {
$ws.Tables[$TableName].TableXml.table.ref = $targetRange.Address
$ws.Tables[$TableName].TableStyle = $TableStyle
}
else {
$tbl = $ws.Tables.Add($targetRange, $TableName) $tbl = $ws.Tables.Add($targetRange, $TableName)
$tbl.TableStyle = $TableStyle $tbl.TableStyle = $TableStyle
} }
}
$PivotTableStartCell = "A1"
if($PivotFilter) {$PivotTableStartCell = "A3"}
if ($PivotTableDefinition) { if ($PivotTableDefinition) {
foreach ($item in $PivotTableDefinition.GetEnumerator()) { foreach ($item in $PivotTableDefinition.GetEnumerator()) {
$targetName = $item.Key $pivotTableName = $item.Key
$pivotTableName = $targetName #+ 'PivotTable' $pivotTableDataName = $item.Key + 'PivotTableData'
#Make sure the Pivot table sheet doesn't already exist if ($item.Value.PivotFilter) {$PivotTableStartCell = "A3"} else { $PivotTableStartCell = "A1"}
try { $pkg.Workbook.Worksheets.Delete( $pivotTableName) } catch {}
$wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber
$pivotTableDataName = $targetName + 'PivotTableData'
if (!$item.Value.SourceWorkSheet) { #Make sure the Pivot table sheet doesn't already exist
#try { $pkg.Workbook.Worksheets.Delete( $pivotTableName) } catch {}
[OfficeOpenXml.ExcelWorksheet]$wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber
#If it is a pivot for the default sheet and it doesn't exist - create it
if (-not $item.Value.SourceWorkSheet -and -not $wsPivot.PivotTables[$pivotTableDataName] ) {
$pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $ws.Cells[$dataRange], $pivotTableDataName) $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $ws.Cells[$dataRange], $pivotTableDataName)
} }
#If it is a pivot for the default sheet and it exists - update the range.
elseif (-not $item.Value.SourceWorkSheet -and $wsPivot.PivotTables[$pivotTableDataName] ) {
$wsPivot.PivotTables[$pivotTableDataName].CacheDefinition.CacheDefinitionXml.pivotCacheDefinition.cacheSource.worksheetSource.ref = $WS.Cells[$dataRange].Address
}
#if it is a pivot for a named sheet and it doesn't exist, create it.
elseif ($item.Value.SourceWorkSheet -and -not $wsPivot.PivotTables[$pivotTableDataName] ) {
$workSheet = $pkg.Workbook.Worksheets.where( {$_.name -match $item.Value.SourceWorkSheet})[0] #removed find worksheet
if (-not $workSheet) {Write-Warning -Message "Could not find Worksheet '$($item.Value.SourceWorkSheet)' specified in pivot-table definition $($item.key)." }
else { else {
$workSheet = Find-WorkSheet $item.Value.SourceWorkSheet $targetDataRange = "{0}:{1}" -f $workSheet.Dimension.Start.Address, $workSheet.Dimension.End.Address
if ($workSheet) {
$targetStartAddress = $workSheet.Dimension.Start.Address
$targetDataRange = "{0}:{1}" -f $targetStartAddress, $workSheet.Dimension.End.Address
$pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $workSheet.Cells[$targetDataRange], $pivotTableDataName) $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $workSheet.Cells[$targetDataRange], $pivotTableDataName)
} }
} }
switch ($item.Value.Keys) { #if we created the pivot table, set up the rows, columns and data if we didn't, put out a message 'existed' or 'error'
"PivotRows" { if ($pivotTable) {
foreach ($Row in $item.Value.PivotRows) { foreach ($Row in $item.Value.PivotRows) {
$null = $pivotTable.RowFields.Add($pivotTable.Fields[$Row]) try {$null = $pivotTable.RowFields.Add($pivotTable.Fields[$Row]) }
catch {Write-Warning -message "Could not add '$row' to Rows in PivotTable $pivotTableName." }
} }
}
"PivotColumns" {
foreach ($Column in $item.Value.PivotColumns) { foreach ($Column in $item.Value.PivotColumns) {
$null = $pivotTable.ColumnFields.Add($pivotTable.Fields[$Column]) try {$null = $pivotTable.ColumnFields.Add($pivotTable.Fields[$Column])}
catch {Write-Warning -message "Could not add '$Column' to Columns in PivotTable $pivotTableName." }
} }
} if ($item.Value.PivotData -is [HashTable] -or $item.Value.PivotData -is [System.Collections.Specialized.OrderedDictionary]) {
$item.Value.PivotData.Keys | ForEach-Object {
"PivotData" { try {
$pivotData = $item.Value.PivotData
if ($PivotData -is [HashTable] -or $PivotData -is [System.Collections.Specialized.OrderedDictionary]) {
$PivotData.Keys | ForEach-Object {
$df = $pivotTable.DataFields.Add($pivotTable.Fields[$_]) $df = $pivotTable.DataFields.Add($pivotTable.Fields[$_])
$df.Function = $PivotData.$_ $df.Function = $item.Value.PivotData.$_
}
catch {Write-Warning -message "Problem adding data fields to PivotTable $pivotTableName." }
} }
} }
else { else {
foreach ($Item in $PivotData) { foreach ($field in $item.Value.PivotData) {
$df = $pivotTable.DataFields.Add($pivotTable.Fields[$Item]) try {
$df = $pivotTable.DataFields.Add($pivotTable.Fields[$field])
$df.Function = 'Count' $df.Function = 'Count'
} }
catch {Write-Warning -message "Problem adding data field '$field' to PivotTable $pivotTableName." }
} }
}
foreach ( $pFilter in $item.Value.PivotFilter) {
try { $null = $pivotTable.PageFields.Add($pivotTable.Fields[$pFilter])}
catch {Write-Warning -message "Could not add '$pFilter' to Filter/Page fields in PivotTable $pivotTableName." }
}
if ($item.Value.NoTotalsInPivot -or $NoTotalsInPivot) { $pivotTable.RowGrandTotals = $false }
if ($item.Value.PivotDataToColumn -or $PivotDataToColumn) { $pivotTable.DataOnRows = $false }
}
elseif ($wsPivot.PivotTables[$pivotTableDataName]) {
Write-Warning -Message "Pivot table defined in $($item.key) already exists."
}
else { Write-Warning -Message "Could not create the pivot table defined in $($item.key)."}
if ($PivotDataToColumn) { #Create the chart if it doesn't exist, leave alone if it does.
$pivotTable.DataOnRows = $false if ($item.Value.IncludePivotChart -and -not $wsPivot.Drawings['PivotChart'] ) {
} if ($item.Value.ChartType) { $ChartType = $item.Value.ChartType} # $ChartType may be passed as a parameter, has default of "Pie", over-ride that if it is in the pivot definition
} [OfficeOpenXml.Drawing.Chart.ExcelChart] $chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable)
if (-not $item.Value.ChartHeight) {$item.Value.ChartHeight = 400 }
"IncludePivotChart" { if (-not $item.Value.ChartWidth) {$item.Value.ChartWidth = 600 }
$ChartType = "Pie" if (-not $item.Value.ChartRow) {$item.Value.ChartRow = 0 }
if ($item.Value.ChartType) { if (-not $item.Value.ChartColumn) {$item.Value.ChartColumn = 4 }
$ChartType = $item.Value.ChartType if (-not $item.Value.ChartRowOffSetPixels) {$item.Value.ChartRowOffSetPixels = 0 }
} if (-not $item.Value.ChartColumnOffSetPixels) {$item.Value.ChartColumnOffSetPixels = 0 }
$chart.SetPosition($item.Value.ChartRow , $item.Value.ChartRowOffSetPixels , $item.Value.ChartColumn, $item.Value.ChartColumnOffSetPixels)
$chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable) $chart.SetSize( $item.Value.ChartWidth, $item.Value.ChartHeight)
$chart.SetPosition(0, 0, 4, 0) #Changed position to top row, next to a chart which doesn't pivot on columns
$chart.SetSize(600, 400)
if ($chart.DataLabel) { if ($chart.DataLabel) {
$chart.DataLabel.ShowCategory = [boolean]$item.value.ShowCategory $chart.DataLabel.ShowCategory = [boolean]$item.Value.ShowCategory
$chart.DataLabel.ShowPercent = [boolean]$item.value.ShowPercent $chart.DataLabel.ShowPercent = [boolean]$item.Value.ShowPercent
} }
if ([boolean]$item.value.NoLegend) {$chart.Legend.Remove()} if ([boolean]$item.Value.NoLegend -or $NoLegend) {$chart.Legend.Remove()}
if ($item.value.ChartTitle) {$chart.Title.Text = $item.value.chartTitle} if ( $item.Value.ChartTitle) {$chart.Title.Text = $item.Value.chartTitle}
}
}
if($item.Value.NoTotalsInPivot) {
$pivotTable.RowGrandTotals = $false
} }
} }
} }
if ($IncludePivotTable -or $IncludePivotChart) { if ($IncludePivotTable -or $IncludePivotChart) {
#changed so -includePivotChart Implies -includePivotTable. if ($PivotFilter) {$PivotTableStartCell = "A3"} else {$PivotTableStartCell = "A1"}
$pivotTableName = $WorkSheetname + 'PivotTable' $pivotTableName = $WorkSheetname + 'PivotTable'
#Make sure the Pivot table sheet doesn't already exist
try { $pkg.Workbook.Worksheets.Delete( $pivotTableName) } catch {}
$wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber $wsPivot = $pkg | Add-WorkSheet -WorkSheetname $pivotTableName -NoClobber:$NoClobber
$wsPivot.View.TabSelected = $true $wsPivot.View.TabSelected = $true
$pivotTableDataName = $WorkSheetname + 'PivotTableData' $pivotTableDataName = $WorkSheetname + 'PivotTableData'
if ($wsPivot.PivotTables[$pivotTableDataName] ) {
$pivotTable = $wsPivot.PivotTables[$pivotTableDataName]
$pivotTable.CacheDefinition.CacheDefinitionXml.pivotCacheDefinition.cacheSource.worksheetSource.ref = $WS.Cells[$dataRange].Address
Write-Warning -Message "Pivot table for $worksheetName already exists; updating the data range, but other properties will not be changed"
}
else {
$pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $ws.Cells[$dataRange], $pivotTableDataName) $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $ws.Cells[$dataRange], $pivotTableDataName)
if ($PivotRows) {
foreach ($Row in $PivotRows) { foreach ($Row in $PivotRows) {
$null = $pivotTable.RowFields.Add($pivotTable.Fields[$Row]) try {$null = $pivotTable.RowFields.Add($pivotTable.Fields[$Row]) }
} catch {Write-Warning -message "Could not add '$row' to PivotTable Rows." }
} }
if ($PivotColumns) {
foreach ($Column in $PivotColumns) { foreach ($Column in $PivotColumns) {
$null = $pivotTable.ColumnFields.Add($pivotTable.Fields[$Column]) try {$null = $pivotTable.ColumnFields.Add($pivotTable.Fields[$Column])}
} catch {Write-Warning -message "Could not add '$Column' to PivotTable Columns." }
} }
if ($PivotData) {
if ($PivotData -is [HashTable] -or $PivotData -is [System.Collections.Specialized.OrderedDictionary]) { if ($PivotData -is [HashTable] -or $PivotData -is [System.Collections.Specialized.OrderedDictionary]) {
$PivotData.Keys | ForEach-Object { $PivotData.Keys | ForEach-Object {
try {
$df = $pivotTable.DataFields.Add($pivotTable.Fields[$_]) $df = $pivotTable.DataFields.Add($pivotTable.Fields[$_])
$df.Function = $PivotData.$_ $df.Function = $PivotData.$_
} }
catch {Write-Warning "Problem adding to Pivot table data fields." }
}
} }
else { else {
foreach ($Item in $PivotData) { foreach ($Item in $PivotData) {
try {
$df = $pivotTable.DataFields.Add($pivotTable.Fields[$Item]) $df = $pivotTable.DataFields.Add($pivotTable.Fields[$Item])
$df.Function = 'Count' $df.Function = 'Count'
} }
} catch {Write-Warning "Problem adding '$item' to Pivot table data fields." }
if ($PivotDataToColumn) {
$pivotTable.DataOnRows = $false
} }
} }
if($NoTotalsInPivot) { if ($PivotDataToColumn) { $pivotTable.DataOnRows = $false }
$pivotTable.RowGrandTotals = $false
foreach ($pFilter in $PivotFilter) {
try {$null = $pivotTable.PageFields.Add($pivotTable.Fields[$pFilter]) }
catch {Write-Warning "Problem adding 'pFilter' to Pivot table page/filter fields." }
}
if ($NoTotalsInPivot) { $pivotTable.RowGrandTotals = $false }
} }
if ($IncludePivotChart) { if ($IncludePivotChart) {
if (-not $wsPivot.Drawings['PivotChart']) {
$chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable) $chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable)
if ($chart.DataLabel) { if ($chart.DataLabel) {
$chart.DataLabel.ShowCategory = $ShowCategory $chart.DataLabel.ShowCategory = $ShowCategory
$chart.DataLabel.ShowPercent = $ShowPercent $chart.DataLabel.ShowPercent = $ShowPercent
} }
$chart.SetPosition(0, 26, 2, 26) # if Pivot table is rows+data only it will be 2 columns wide if has pivot columns we don't know how wide it will be $chart.SetPosition(0, 26, 2, 26) # if Pivot table is rows+data only it will be 2 columns wide if has pivot columns we don't know how wide it will be
if ($NoLegend) { if ($NoLegend) { $chart.Legend.Remove() }
$chart.Legend.Remove()
}
} }
} }
if($pivotTable -and $PivotFilter) {
foreach($pFilter in $PivotFilter) {
$null = $pivotTable.PageFields.Add($pivotTable.Fields[$pFilter])
} }
}
if ($Password) { if ($Password) {
$ws.Protection.SetPassword($Password) $ws.Protection.SetPassword($Password)
@@ -896,6 +920,7 @@
$ws.View.FreezePanes($freezeRow, $freezeColumn) $ws.View.FreezePanes($freezeRow, $freezeColumn)
} }
} }
if ($BoldTopRow) { if ($BoldTopRow) {
if ($Title) { if ($Title) {
$range = $ws.Dimension.Address -replace '\d+', '2' $range = $ws.Dimension.Address -replace '\d+', '2'
@@ -906,6 +931,7 @@
$ws.Cells[$range].Style.Font.Bold = $true $ws.Cells[$range].Style.Font.Bold = $true
} }
if ($AutoSize) { if ($AutoSize) {
$ws.Cells.AutoFitColumns() $ws.Cells.AutoFitColumns()
} }
@@ -914,7 +940,6 @@
$pkg.Workbook.WorkSheets[$Sheet].Hidden = 'Hidden' $pkg.Workbook.WorkSheets[$Sheet].Hidden = 'Hidden'
} }
$chartCount = 0
foreach ($chartDef in $ExcelChartDefinition) { foreach ($chartDef in $ExcelChartDefinition) {
$ChartName = 'Chart' + (Split-Path -Leaf ([System.IO.path]::GetTempFileName())) -replace 'tmp|\.', '' $ChartName = 'Chart' + (Split-Path -Leaf ([System.IO.path]::GetTempFileName())) -replace 'tmp|\.', ''
$chart = $ws.Drawings.AddChart($ChartName, $chartDef.ChartType) $chart = $ws.Drawings.AddChart($ChartName, $chartDef.ChartType)
@@ -1001,8 +1026,6 @@
$ws.Dimension.Address $ws.Dimension.Address
} }
$pkg.Save() $pkg.Save()
if ($ReZip) { if ($ReZip) {
@@ -1010,7 +1033,8 @@
$zipAssembly = "System.IO.Compression.Filesystem" $zipAssembly = "System.IO.Compression.Filesystem"
try { try {
Add-Type -assembly $zipAssembly -ErrorAction stop Add-Type -assembly $zipAssembly -ErrorAction stop
} catch { }
catch {
write-error "The -ReZip parameter requires .NET Framework 4.5 or later to be installed. Recommend to install Powershell v4+" write-error "The -ReZip parameter requires .NET Framework 4.5 or later to be installed. Recommend to install Powershell v4+"
continue continue
} }
@@ -1043,13 +1067,20 @@ function New-PivotTableDefinition {
$PivotRows, $PivotRows,
[hashtable]$PivotData, [hashtable]$PivotData,
$PivotColumns, $PivotColumns,
$PivotFilter,
[Switch]$NoTotalsInPivot,
[Switch]$IncludePivotChart, [Switch]$IncludePivotChart,
[String]$ChartTitle,
[int]$ChartHeight = 400 ,
[int]$ChartWidth = 600,
[Int]$ChartRow = 0 ,
[Int]$ChartColumn = 6,
[Int]$ChartRowOffSetPixels = 0 ,
[Int]$ChartColumnOffSetPixels = 0,
[OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie', [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie',
[Switch]$NoLegend, [Switch]$NoLegend,
[Switch]$ShowCategory, [Switch]$ShowCategory,
[Switch]$ShowPercent, [Switch]$ShowPercent
[String]$ChartTitle,
[Switch]$NoTotalsInPivot
) )
$parameters = @{} + $PSBoundParameters $parameters = @{} + $PSBoundParameters