diff --git a/AddConditionalFormatting.ps1 b/AddConditionalFormatting.ps1 index 0657b60..01f1ebb 100644 --- a/AddConditionalFormatting.ps1 +++ b/AddConditionalFormatting.ps1 @@ -2,27 +2,44 @@ <# .Synopsis Adds contitional formatting to worksheet. + .Description + Conditional formatting allows excel to + * Mark cells with Icons depending on their value + * Show a databar whose length indicates the value or a 2 or 3 color scale where the color indicate the relative value + * Change the color, font, or number format of cells which meet given criteria + Add-ConditionalFormatting allows these to be set; for fine tuning of the rules you can use the -PassThru switch, + which will return the rule so that you can modify things which are specific to that type of rule, + for example the values which correspond to each icon in an Icon set. .Example - $excel = $avdata | Export-Excel -Path (Join-path $FilePath "\Machines.XLSX" ) -WorksheetName "Server Anti-Virus" -AutoSize -FreezeTopRow -AutoFilter -PassThru + C:\> $excel = $avdata | Export-Excel -Path (Join-path $FilePath "\Machines.XLSX" ) -WorksheetName "Server Anti-Virus" -AutoSize -FreezeTopRow -AutoFilter -PassThru - Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "b2:b1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "2003" - Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "i2:i1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "Disabled" - $excel.Workbook.Worksheets[1].Cells["D1:G1048576"].Style.Numberformat.Format = [cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern - $excel.Workbook.Worksheets[1].Row(1).style.font.bold = $true - $excel.Save() ; $excel.Dispose() + C:\> Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "b2:b1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "2003" + C:\> Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "i2:i1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "Disabled" + C:\> $excel.Workbook.Worksheets[1].Cells["D1:G1048576"].Style.Numberformat.Format = [cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern + C:\> $excel.Workbook.Worksheets[1].Row(1).style.font.bold = $true + C:\> $excel.Save() ; $excel.Dispose() Here Export-Excel is called with the -passThru parameter so the Excel Package object is stored in $Excel The desired worksheet is selected and the then columns B and i are conditially formatted (excluding the top row) to show red text if - the columns contain "2003" or "Disabled respectively. A fixed date formats are then applied to columns D..G, and the top row is formatted. + the columns contain "2003" or "Disabled respectively. A fixed date format is then applied to columns D..G, and the top row is formatted. Finally the workbook is saved and the Excel object closed. .Example C:\> $r = Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Range "B1:B100" -ThreeIconsSet Flags -Passthru C:\> $r.Reverse = $true ; $r.Icon1.Type = "Num"; $r.Icon2.Type = "Num" ; $r.Icon2.value = 100 ; $r.Icon3.type = "Num" ;$r.Icon3.value = 1000 - Again Export excel has been called with -passthru leaving a package object in $Excel + Again Export-Excel has been called with -passthru leaving a package object in $Excel This time B1:B100 has been conditionally formatted with 3 icons, using the flags icon set. Add-ConditionalFormatting does not provide access to every option in the formatting rule, so passthru has been used and the rule is to apply the flags in reverse order, and boundaries for the number which will set the split are set to 100 and 1000 + .Example + C:\> Add-ConditionalFormatting -WorkSheet $sheet -Range "D2:D1048576" -DataBarColor Red + + This time $sheet holds an ExcelWorkseet object and databars are add to all of column D except for the tip row. + .Example + C:\> Add-ConditionalFormatting -Address $worksheet.cells["FinishPosition"] -RuleType Equal -ConditionValue 1 -ForeGroundColor Purple -Bold -Priority 1 -StopIfTrue + + In this example a named range is used to select the cells where the formula should apply. If a cell in the "FinishPosition" range is 1, then the text is turned to puple, boldface. + This rule is move to first in the priority list, and where cells have a value of 1, no other rules will be processed. #> Param ( #The worksheet where the format is to be applied diff --git a/Copy-ExcelWorkSheet.ps1 b/Copy-ExcelWorkSheet.ps1 index 26b5f75..5e6c2ac 100644 --- a/Copy-ExcelWorkSheet.ps1 +++ b/Copy-ExcelWorkSheet.ps1 @@ -1,5 +1,4 @@ function Copy-ExcelWorkSheet { - [CmdletBinding()] <# .SYNOPSIS Copies a worksheet between workbooks or within the same workbook. @@ -27,6 +26,7 @@ because -Show is not specified, so other steps can be carried using the package object, at the end the file is saved by Close-ExcelPackage #> + [CmdletBinding()] param( #An ExcelWorkbook or ExcelPackage object or the path to an XLSx file where the data is found. [Parameter(Mandatory=$true)] diff --git a/Examples/Charts/MultiSeries1.ps1 b/Examples/Charts/MultiSeries1.ps1 index 48a04dc..ddcbf4b 100644 --- a/Examples/Charts/MultiSeries1.ps1 +++ b/Examples/Charts/MultiSeries1.ps1 @@ -11,7 +11,7 @@ A,B,C,Date $c = New-ExcelChartDefinition -Title Impressions ` -ChartType Line ` -XRange "Impressions[Date]" ` - -YRange "Impressions[B]" # @("Impressions[B]","Impressions[A]") ` + -YRange @("Impressions[B]","Impressions[A]") ` -SeriesHeader 'B data','A data' ` -Row 0 -Column 0 diff --git a/Examples/Charts/plot.ps1 b/Examples/Charts/plot.ps1 index 14921e0..f8c0401 100644 --- a/Examples/Charts/plot.ps1 +++ b/Examples/Charts/plot.ps1 @@ -13,14 +13,14 @@ function plot { $file = 'C:\temp\plot.xlsx' Remove-Item $file -ErrorAction Ignore - $c = New-ExcelChart -XRange X -YRange Y -ChartType Line -NoLegend -Title Plot -Column 2 -ColumnOffSetPixels 35 + # $c = New-ExcelChart -XRange X -YRange Y -ChartType Line -NoLegend -Title Plot -Column 2 -ColumnOffSetPixels 35 $(for ($i = $minx; $i -lt $maxx-.1; $i+=.1) { [pscustomobject]@{ X=$i.ToString("N1") Y=(&$f $i) } - }) | Export-Excel $file -Show -AutoNameRange -ExcelChartDefinition $c + }) | Export-Excel $file -Show -AutoNameRange -LineChart -NoLegend #-ExcelChartDefinition $c } function pi {[math]::pi} diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index 1faa3a9..c339531 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -48,6 +48,8 @@ Name(s) columns from the spreadhseet which will provide the Filter name(s) in a pivot table created from command line parameters. .PARAMETER PivotData In a pivot table created from command line parameters, the fields to use in the table body are given as a Hash table in the form ColumnName = Average|Count|CountNums|Max|Min|Product|None|StdDev|StdDevP|Sum|Var|VarP . + .PARAMETER PivotDataToColumn + If there are multiple datasets in a PivotTable, by default they are shown seperatate rows under the given row heading; this switch makes them seperate columns. .PARAMETER NoTotalsInPivot In a pivot table created from command line parameters, prevents the addition of totals to rows and columns. .PARAMETER PivotTableDefinition @@ -66,7 +68,7 @@ .PARAMETER ConditionalFormat One or more conditional formatting rules defined with New-ConditionalFormattingIconSet. .PARAMETER ConditionalText - Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered. + Applies a Conditional formatting rule defined with New-ConditionalText. When specific conditions are met the format is applied. .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. .PARAMETER BoldTopRow @@ -159,6 +161,11 @@ .PARAMETER ReZip If specified, Export-Excel will expand the contents of the .XLSX file (which is multiple files in a zip archive) and rebuilt it. + .PARAMETER NoClobber + Not used. Left in to avoid problems with older scripts, it may be removed in future versions. + .PARAMETER CellStyleSB + A script block which is run at the end of the export to apply styles to cells (although it can be used for other purposes). + The script block is given three paramaters; an object containing the current worksheet, the Total number of Rows and the number of the last column. .PARAMETER Show Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first. .PARAMETER ReturnRange @@ -358,7 +365,10 @@ This a more sophisticated version of the previous example showing different ways of using Set-Format, and also adding conditional formatting. In the final command a Pivot chart is added and the workbook is opened in Excel. + .EXAMPLE + 0..360 | ForEach-Object {[pscustomobject][ordered]@{X=$_; Sinx="=Sin(Radians(x)) "} } | Export-Excel -now -LineChart -AutoNameRange + Creates a line chart showing the value of Sine(x) for values of X between 0 and 360 degrees. .LINK https://github.com/dfinke/ImportExcel #> @@ -915,12 +925,21 @@ Add-ExcelChart -Worksheet $ws @params } + if ($Calculate) { + try { [OfficeOpenXml.CalculationExtension]::Calculate($ws) } + catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook. $_"} + } + if ($Barchart -or $PieChart -or $LineChart -or $ColumnChart) { if ($NoHeader) {$FirstDataRow = $startRow} else {$FirstDataRow = $startRow + 1 } $range = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $startColumn, $FirstDataRow, $lastCol ) $xCol = $ws.cells[$range] | Where-Object {$_.value -is [string] } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 - $yCol = $ws.cells[$range] | Where-Object {$_.value -is [valueType] } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 + if (-not $xcol) { + $xcol = $StartColumn + $range = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, ($startColumn +1), $FirstDataRow, $lastCol ) + } + $yCol = $ws.cells[$range] | Where-Object {$_.value -is [valueType] -or $_.Formula } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1 if (-not ($xCol -and $ycol)) { Write-Warning -Message "Can't identify a string column and a number column to use as chart labels and data. "} else { $params = @{ @@ -965,10 +984,6 @@ catch {Write-Warning -Message "Failed processing CellStyleSB in worksheet '$WorksheetName': $_"} } - if ($Calculate) { - try { [OfficeOpenXml.CalculationExtension]::Calculate($ws) } - catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook."} - } if ($Password) { try { @@ -1169,15 +1184,17 @@ function Select-Worksheet { } Function Add-ExcelName { - [CmdletBinding()] <# .SYNOPSIS - Adds named ranges to Excel worksheets + Adds a named range to an existing Excel worksheet + .DESCRIPTION + It is often helpful to be able to refer to sets of cells with a name rather than using their co-ordinates; Add-ExcelName sets up these names. .EXAMPLE Add-ExcelName -Range $ws.Cells[$dataRange] -RangeName $rangeName $WS is a worksheet, and $dataRange holds a range of cells - e.g. "A1:Z10" which will become a named range, using the name in $rangeName. #> + [CmdletBinding()] param( #The range of cells to assign as a name. [Parameter(Mandatory=$true)] @@ -1208,8 +1225,6 @@ Function Add-ExcelName { } function Add-ExcelTable { - [CmdletBinding()] - [OutputType([OfficeOpenXml.Table.ExcelTable])] <# .SYNOPSIS Adds Tables to Excel workbooks. @@ -1225,6 +1240,8 @@ function Add-ExcelTable { Again $ws is a worksheet, range here is the whole of the active part of the worksheet. The table style and name are set, the filter is turned off, a totals row added and first column set in bold. #> + [CmdletBinding()] + [OutputType([OfficeOpenXml.Table.ExcelTable])] param ( #The range of cells to assign to a table [Parameter(Mandatory=$true)] diff --git a/Join-Worksheet.ps1 b/Join-Worksheet.ps1 index 8786d1e..3acf06a 100644 --- a/Join-Worksheet.ps1 +++ b/Join-Worksheet.ps1 @@ -1,5 +1,4 @@ function Join-Worksheet { - [CmdletBinding(DefaultParameterSetName = 'Default')] <# .SYNOPSIS Combines data on all the sheets in an Excel worksheet onto a single sheet. @@ -32,8 +31,9 @@ The first two command get logical disk and network card information; each type is exported to its own sheet in a workbook. The Join-worksheet command copies both onto a page named "Summary". Because the data is disimilar -NoHeader is specified, ensuring the whole of each page is copied. Specifying -LabelBlocks causes each sheet's name to become a title on the summary page above the copied data. - The source data is hidden, a title is addded in 22 point boldface and the columns are sized to fit the data. + The source data is hidden, a title is added in 22 point boldface and the columns are sized to fit the data. #> + [CmdletBinding(DefaultParameterSetName = 'Default')] param ( # Path to a new or existing .XLSX file. [Parameter(ParameterSetName = "Default", Position = 0)] @@ -85,8 +85,9 @@ [Hashtable]$PivotTableDefinition, #A hashtable containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts. [Object[]]$ExcelChartDefinition, + #One or more conditional formatting rules defined with New-ConditionalFormattingIconSet. [Object[]]$ConditionalFormat, - #Applies a 'Conditional formatting rule' in Excel on all the cells. When specific conditions are met a rule is triggered. + #Applies a Conditional formatting rule defined with New-ConditionalText. When specific conditions are met the format is applied [Object[]]$ConditionalText, #Makes each column a named range. [switch]$AutoNameRange, @@ -108,6 +109,7 @@ [String]$TableName, [Parameter(ParameterSetName = 'Table')] [Parameter(ParameterSetName = 'PackageTable')] + #Selects the style for the named table - defaults to 'Medium6'. [OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6', #Selects the style for the named table - defaults to 'Medium6'. [switch]$ReturnRange, diff --git a/Merge-worksheet.ps1 b/Merge-worksheet.ps1 index 54cf472..03d83e8 100644 --- a/Merge-worksheet.ps1 +++ b/Merge-worksheet.ps1 @@ -72,6 +72,7 @@ $DifferenceObject , [parameter(ParameterSetName='D',Position=2)] [parameter(ParameterSetName='E',Position=3)] + #If there isn't a filename to use to label data from the "Difference" side, DiffPrefix is used, it defaults to "=>" $DiffPrefix = "=>" , #File to hold merged data. [parameter(Position=3)] @@ -281,8 +282,6 @@ } Function Merge-MultipleSheets { -[cmdletbinding()] -[Alias("Merge-MulipleSheets")] <# .Synopsis Merges worksheets into a single worksheet with differences marked up. @@ -321,8 +320,10 @@ Function Merge-MultipleSheets { (the information was obtained by running Get-Hotfix | Sort-Object -Property description,hotfixid | Select-Object -Property Description,HotfixID) This ignores any sheets which are not named "Serv*", and uses the HotfixID as the key ; in this version the row numbers are hidden. #> - + [cmdletbinding()] + [Alias("Merge-MulipleSheets")] param ( + #Paths to the files to be merged. [Parameter(Mandatory=$true,ValueFromPipeline=$true)] [string[]]$Path , #The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row. diff --git a/New-ConditionalFormattingIconSet.ps1 b/New-ConditionalFormattingIconSet.ps1 index c355150..842bc45 100644 --- a/New-ConditionalFormattingIconSet.ps1 +++ b/New-ConditionalFormattingIconSet.ps1 @@ -1,4 +1,27 @@ function New-ConditionalFormattingIconSet { + <# + .SYNOPSIS + Creates an object which describes a conditional formatting rule a for 3,4 or 5 icon set + .DESCRIPTION + Export-Excel takes a -ConditionalFormat parameter which can hold one or more descriptions for conditional formats; + this command builds the + .PARAMETER Range + The range of cells that the conditional format applies to + .PARAMETER ConditionalFormat + The type of rule: one of "ThreeIconSet","FourIconSet" or "FiveIconSet" + .PARAMETER IconType + The name of an iconSet - different icons are available depending on whether 3,4 or 5 icon set is selected + .PARAMETER Reverse + Use the icons in the reverse order. + .Example + $cfRange = [OfficeOpenXml.ExcelAddress]::new($topRow,$column,$lastDataRow,$column) + $cfdef = New-ConditionalFormattingIconSet -Range $cfrange -ConditionalFormat ThreeIconSet -IconType Arrows + Export-Excel -ExcelPackage $excel -ConditionalFormat $cfdef -show + + The first line creates a range - one column wide in the column $column, running from $topRow to $lastDataRow. + The second creates a definition object using this range + and the third uses Export-Excel with an open package to apply the format and save and open the file. + #> param( [Parameter(Mandatory=$true)] $Range, diff --git a/New-ConditionalText.ps1 b/New-ConditionalText.ps1 index 4971462..60b2a4d 100644 --- a/New-ConditionalText.ps1 +++ b/New-ConditionalText.ps1 @@ -1,4 +1,46 @@ function New-ConditionalText { + <# + .SYNOPSIS + Creates an object which describes a conditional formatting rule for single valued rules + .DESCRIPTION + Some Conditional formatting rules don't apply styles to a cell (IconSets and Databars) + Some take two parameters (Between) + Some take none (ThisWeek , containsErrors, AboveAverage etc.) + The others take a single paramter (top, BottomPercent, GreaterThan, Contains etc) + This command creates an object to describe the last two categories, which can be passed to Export-Excel + .PARAMETER Range + The range of cells that the conditional format applies to; if none is specified the range will be apply to all the data in the sheet + .PARAMETER ConditionalType + One the supported rules by default - "ContainsText" is selected + .PARAMETER Text + The text (or other value) to use in the rule. Not that Equals, GreaterThan/LessThan rules require text to wrapped in double quotes + .PARAMETER ConditionalTextColor + The font color for the cell - by default: Dark red + .PARAMETER BackgroundColor + The fill color for the cell - by default: light pink + .PARAMETER PatternType + The Background pattern for the cell - by deault: Solid + .EXAMPLE + $ct = New-ConditionalText -Text 'Ferrari' + Export-Excel -ExcelPackage $excel -ConditionalTest $ct -show + + The first line creates a definition object which will highlight the word "Ferrari" in any cell. + and the third uses Export-Excel with an open package to apply the format and save and open the file. + .EXAMPLE + $ct = New-ConditionalText -Text 'Ferrari' + Export-Excel -ExcelPackage $excel -ConditionalTest $ct -show + + The first line creates a definition object which will highlight the word "Ferrari" in any cell. + and the third uses Export-Excel with an open package to apply the format and save and open the file. + .EXAMPLE + $ct = New-ConditionalText -Text "Ferrari" + $ct2 = New-ConditionalText -Range $worksheet.Names["FinishPosition"].Address -ConditionalType LessThanOrEqual -Text 3 -ConditionalTextColor Red -BackgroundColor White + Export-Excel -ExcelPackage $excel -ConditionalTest $ct,$c2 -show + + This builds on the previous example, and specifies a condition of <=3 with a format of Red text on a white background; this applies to a named range "Finish Position" + the range could be written "C:C" to specify a named column, or "C2:C102" to specify certain cells in the column. + #> + [cmdletbinding()] param( #[Parameter(Mandatory=$true)] $Text, diff --git a/New-ExcelChart.ps1 b/New-ExcelChart.ps1 index c77e38b..c66b82f 100644 --- a/New-ExcelChart.ps1 +++ b/New-ExcelChart.ps1 @@ -1,4 +1,96 @@ function New-ExcelChartDefinition { + <# + .SYNOPSIS + Creates a Definition of a chart which can be added using Export Excel + .DESCRIPTION + All the parameters which are passed to Add-ExcelChart can be added to an object and + passed to Export-Excel with the -ExcelChartDefinition parameter. This command sets up those objects. + .PARAMETER Title + The title for the chart. + .PARAMETER TitleBold + Sets the title in bold face. + .PARAMETER TitleSize + Sets the point size for the title. + .PARAMETER ChartType + One of the built in chart types, such as Pie, ClusteredColumn, Line etc. Defaults to "ColumnStacked". + .PARAMETER XRange + The range of cells containing values for the X-Axis - usually labels. + .PARAMETER YRange + The range(s) of cells holding values for the Y-Axis - usually "data". + .PARAMETER Width + Width of the chart in Pixels. Defaults to 500. + .PARAMETER Height + Height of the chart in Pixels. Defaults to 350. + .PARAMETER Row + Row position of the top left corner of the chart. 0 places at the top of the sheet, 1 below row 1 and so on. + .PARAMETER RowOffSetPixels + Offset to postion the chart by a fraction of of a row . + .PARAMETER Column + Column Postion of the top left corner of the chart. 0 places at the edge of the sheet 1 to the right of column A and so on. + .PARAMETER ColumnOffSetPixels + Offset to postion the chart by a fraction of of a column. + .PARAMETER NoLegend + If specified, turns of display of the key. If you only have one data series it may be preferable to use the title to say what the chart is. + .PARAMETER SeriesHeader + Specify explicit name(s) for the data series, which will appear in the legend/key + .PARAMETER LegendPostion + Location of the key, either left, right, top, bottom or TopRight. + .PARAMETER LegendSize + Font size for the key + .PARAMETER LegendBold + Sets the key in bold type. + .PARAMETER ShowCategory + Attaches a category label in charts which support this. + .PARAMETER ShowPercent + Attaches a pecentage label in charts which support this. + .PARAMETER XAxisTitleText + Specifies a title for the X axis. + .PARAMETER XAxisTitleBold + Sets the X axis title in bold face. + .PARAMETER XAxisTitleSize + Sets the font size for the axis title + .PARAMETER XAxisNumberformat + A number formatting string, like "#,##0.00" for numbers along the X axis + .PARAMETER XMajorUnit + Spacing for the major gridlines / tick marks along the X axis + .PARAMETER XMinorUnit + Spacing for the major gridlines / tick marks along the X axis + .PARAMETER XMaxValue + Maximum value for the scale along the Xaxis + .PARAMETER XMinValue + Minimum value for the scale along the Xaxis + .PARAMETER xAxisPosition + Postion for the X axis (top or bottom) + .PARAMETER YAxisTitleText + Specifies a title for the Y axis. + .PARAMETER YAxisTitleBold + Sets the Y axis title in bold face. + .PARAMETER YAxisTitleSize + Sets the font size for the Y axis title + .PARAMETER YAxisNumberformat + A number formatting string, like "#,##0.00" for numbers on the Y axis + .PARAMETER YMajorUnit + Spacing for the major gridlines / tick marks on the Y axis + .PARAMETER YMinorUnit + Spacing for the major gridlines / tick marks on the Y axis + .PARAMETER YMaxValue + Maximum value on the Yaxis + .PARAMETER YMinValue + Minimum value on the Yaxis + .PARAMETER YAxisPosition + Postion for the Y axis (left or right) + .PARAMETER Header + No longer used. This may be removed in future versions + .Example + $cDef = New-ExcelChartDefinition -ChartType line -XRange "X" -YRange "Sinx" -Title "Graph of Sine X" -TitleBold -TitleSize 14 ` + -Column 2 -ColumnOffSetPixels 35 -Width 800 -XAxisTitleText "Degrees" -XAxisTitleBold -XAxisTitleSize 12 -XMajorUnit 30 -XMinorUnit 10 -XMinValue 0 -XMaxValue 361 -XAxisNumberformat "000" ` + -YMinValue -1.25 -YMaxValue 1.25 -YMajorUnit 0.25 -YAxisNumberformat "0.00" -YAxisTitleText "Sine" -YAxisTitleBold -YAxisTitleSize 12 ` + -SeriesHeader "Sin(x)" -LegendSize 8 -legendBold -LegendPostion Bottom + + 0..360 | ForEach-Object {[pscustomobject][ordered]@{x = $_; Sinx = "=Sin(Radians(x)) "}} | Export-Excel -AutoNameRange -now -WorkSheetname SinX -ExcelChartDefinition $cDef -Show + + This reworks an example from Add-Excel-Chart but here the chart definition is defined and then it is used in a call to Export-Excel. + #> [Alias("New-ExcelChart")] #This was the former name. The new name reflects that we are defining a chart, not making one in the workbook. [cmdletbinding()] param( @@ -86,19 +178,116 @@ function New-ExcelChartDefinition { function Add-ExcelChart { <# - .Synopsis - Creates a chart in an Existing excel worksheet + .SYNOPSIS + Creates a chart in an existing Excel worksheet + .DESCRIPTION + Creates a chart. It is possible to configure the type of chart, the range of X values (labels) and Y values. + the title, the legend, the ranges for both axes, the format and postion of the axes. + Normally the command does not return anything, but if -passthru is specified the chart is returned so that it can be customized. + .PARAMETER Worksheet + An exisiting Sheet where the chart will be created. + .PARAMETER Title + The title for the chart. + .PARAMETER TitleBold + Sets the title in bold face. + .PARAMETER TitleSize + Sets the point size for the title. + .PARAMETER ChartType + One of the built in chart types, such as Pie, ClusteredColumn, Line etc. Defaults to "ColumnStacked". + .PARAMETER XRange + The range of cells containing values for the X-Axis - usually labels. + .PARAMETER YRange + The range(s) of cells holding values for the Y-Axis - usually "data". + .PARAMETER PivotTable + Instead of specify X and Y ranges, get data from a PivotTable by passing a PivotTable Object. + .PARAMETER Width + Width of the chart in Pixels. Defaults to 500. + .PARAMETER Height + Height of the chart in Pixels. Defaults to 350. + .PARAMETER Row + Row position of the top left corner of the chart. 0 places at the top of the sheet, 1 below row 1 and so on. + .PARAMETER RowOffSetPixels + Offset to postion the chart by a fraction of of a row . + .PARAMETER Column + Column Postion of the top left corner of the chart. 0 places at the edge of the sheet 1 to the right of column A and so on. + .PARAMETER ColumnOffSetPixels + Offset to postion the chart by a fraction of of a column. + .PARAMETER NoLegend + If specified, turns of display of the key. If you only have one data series it may be preferable to use the title to say what the chart is. + .PARAMETER SeriesHeader + Specify explicit name(s) for the data series, which will appear in the legend/key + .PARAMETER LegendPostion + Location of the key, either left, right, top, bottom or TopRight. + .PARAMETER LegendSize + Font size for the key + .PARAMETER LegendBold + Sets the key in bold type. + .PARAMETER ShowCategory + Attaches a category label in charts which support this. + .PARAMETER ShowPercent + Attaches a pecentage label in charts which support this. + .PARAMETER XAxisTitleText + Specifies a title for the X axis. + .PARAMETER XAxisTitleBold + Sets the X axis title in bold face. + .PARAMETER XAxisTitleSize + Sets the font size for the axis title + .PARAMETER XAxisNumberformat + A number formatting string, like "#,##0.00" for numbers along the X axis + .PARAMETER XMajorUnit + Spacing for the major gridlines / tick marks along the X axis + .PARAMETER XMinorUnit + Spacing for the major gridlines / tick marks along the X axis + .PARAMETER XMaxValue + Maximum value for the scale along the Xaxis + .PARAMETER XMinValue + Minimum value for the scale along the Xaxis + .PARAMETER xAxisPosition + Postion for the X axis (top or bottom) + .PARAMETER YAxisTitleText + Specifies a title for the Y axis. + .PARAMETER YAxisTitleBold + Sets the Y axis title in bold face. + .PARAMETER YAxisTitleSize + Sets the font size for the Y axis title + .PARAMETER YAxisNumberformat + A number formatting string, like "#,##0.00" for numbers on the Y axis + .PARAMETER YMajorUnit + Spacing for the major gridlines / tick marks on the Y axis + .PARAMETER YMinorUnit + Spacing for the major gridlines / tick marks on the Y axis + .PARAMETER YMaxValue + Maximum value on the Yaxis + .PARAMETER YMinValue + Minimum value on the Yaxis + .PARAMETER YAxisPosition + Postion for the Y axis (left or right) + .PARAMETER PassThru + Add-Excel chart doesn't normally return anything, but if -PassThru is specified it returns the newly created chart to allow it to be fine tuned + .EXAMPLE + $excel = 0..360 | ForEach-Object {[pscustomobject][ordered]@{x = $_; Sinx = "=Sin(Radians(x)) "}} | Export-Excel -AutoNameRange -Path Text.xlsx -WorkSheetname SinX -PassThru + Add-ExcelChart -Worksheet $excel.Workbook.Worksheets["Sinx"] -ChartType line -XRange "X" -YRange "Sinx" -Title "Graph of Sine X" -TitleBold -TitleSize 14 ` + -Column 2 -ColumnOffSetPixels 35 -Width 800 -XAxisTitleText "Degrees" -XAxisTitleBold -XAxisTitleSize 12 -XMajorUnit 30 -XMinorUnit 10 -XMinValue 0 -XMaxValue 361 -XAxisNumberformat "000" ` + -YMinValue -1.25 -YMaxValue 1.25 -YMajorUnit 0.25 -YAxisNumberformat "0.00" -YAxisTitleText "Sine" -YAxisTitleBold -YAxisTitleSize 12 ` + -SeriesHeader "Sin(x)" -LegendSize 8 -legendBold -LegendPostion Bottom + + The first line puts numbers from 0 to 360 into a sheet, as the first column, and a formula to calculate the Sine of that number of number of degrees in the second column. + It creates ranges for the two columns - "X" and "SinX" respectively + The Add-Excel chart colum adds a chart to that work sheet, specifying a line chart with the X values comming from named range "X" and the the Y values comming the range named "SinX". + The chart has a title, and is positioned to the right of column 2 and sized 8000 pixels wide + Thed X axis s labeled "Degrees", in bold 12 point type and runs from 0 to 361 with labels every 30, and minor tick marks every 10. Degres are shown badded to 3 didits. + The Y axis is labeled "Sine" and to allow some room above and below its scale runs from -1.25 to 1.25, and is marked off in units of 0.25 show to two decimal places. + The key will for the chart will be at the bottom in 8 point bold type and the line will be named "Sin(x)" #> [cmdletbinding(DefaultParameterSetName='Worksheet')] + [OutputType([OfficeOpenXml.Drawing.Chart.ExcelChart])] param( - #An object representing the worksheet where the chart should be added. [Parameter(ParameterSetName='Workshet',Mandatory=$true)] [OfficeOpenXml.ExcelWorksheet]$Worksheet, [Parameter(ParameterSetName='PivotTable',Mandatory=$true)] [OfficeOpenXml.Table.PivotTable.ExcelPivotTable]$PivotTable , [String]$Title = "Chart Title", #$Header, Not used but referenced previously - #The Type of chart (Area, Line, Pie etc) [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = "ColumnStacked", $XRange, $YRange, @@ -204,7 +393,6 @@ function Add-ExcelChart { $chart.SetPosition($Row, $RowOffsetPixels, $Column, $ColumnOffsetPixels) $chart.SetSize($Width, $Height) - if ($PassThru) {return $chart} } catch {Write-Warning -Message "Failed adding Chart to worksheet '$($WorkSheet).name': $_"} diff --git a/Open-ExcelPackage.ps1 b/Open-ExcelPackage.ps1 index b8fc6c4..ff76581 100644 --- a/Open-ExcelPackage.ps1 +++ b/Open-ExcelPackage.ps1 @@ -47,22 +47,33 @@ } Function Close-ExcelPackage { -<# -.Synopsis - Closes an Excel Package, saving, saving under a new name or abandoning changes and opening the file in Excel as required. -#> + <# + .Synopsis + Closes an Excel Package, saving, saving under a new name or abandoning changes and opening the file in Excel as required. + .Description + When working with an Excel packaage object the workbook is held in memory and not saved until the Save() method of the package is called. + Close package saves and disposes of the package object. It can be called with -NoSave to abandon the file without saving, with a new "SaveAs" filename + with a password to protect the file. And with Show to open it in Excel. -Calculate will try to update the workbook, although not everything can be recalculated + .Example + Close-ExcelPackage -show $excel + $excel holds a package object, this saves the workbook and loads it into Excel. + .Example + Close-ExcelPackage -NoSave $excel + $excel holds a package object, this disposes of it without writing it to disk. + #> [CmdLetBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")] Param ( - #File to close. + #Package to close. [parameter(Mandatory=$true, ValueFromPipeline=$true)] [OfficeOpenXml.ExcelPackage]$ExcelPackage, - #Open the file. + #Open the file in Excel. [switch]$Show, #Abandon the file without saving. [Switch]$NoSave, #Save file with a new name (ignored if -NoSave Specified). $SaveAs, + #Password to protect the file. [ValidateNotNullOrEmpty()] [String]$Password, #Attempt to recalculation the workbook before saving diff --git a/PivotTable.ps1 b/PivotTable.ps1 index 44e8e91..79c901b 100644 --- a/PivotTable.ps1 +++ b/PivotTable.ps1 @@ -1,278 +1,306 @@ function Add-PivotTable { - [cmdletbinding(defaultParameterSetName='ChartbyParams')] <# .Synopsis Adds a Pivot table (and optional pivot chart) to a workbook .Description If the pivot table already exists, the source data will be updated. + .Example + $chartdef = New-ExcelChartDefinition -Title "Gross and net by city and product" -ChartType ColumnClustered ` + -Column 11 -Width 500 -Height 360 -YMajorUnit 500 -YMinorUnit 100 -YAxisNumberformat "$#,##0" -LegendPostion Bottom + + $excel = ConvertFrom-Csv @" + Product, City, Gross, Net + Apple, London , 300, 250 + Orange, London , 400, 350 + Banana, London , 300, 200 + Orange, Paris, 600, 500 + Banana, Paris, 300, 200 + Apple, New York, 1200,700 + "@ | Export-Excel -Path "test.xlsx" -TableStyle Medium13 -tablename "RawData" -PassThru + + Add-PivotTable -PivotTableName Sales -Address $excel.Workbook.Worksheets[1].Cells["F1"] ` + -SourceWorkSheet $excel.Workbook.Worksheets[1] -PivotRows City -PivotColumns Product -PivotData @{Gross="Sum";Net="Sum"} ` + -PivotNumberFormat "$#,##0.00" -PivotTotals Both -PivotTableSyle Medium12 -PivotChartDefinition $chartdef + Close-ExcelPackage -show $excel + + This script starts by defining a chart. Then it exports some data to an XLSX file and keeps the file open. + The next step is to add the pivot table, normally this would be on its own sheeet in the workbook, + but here -Address is specified to place it beside the data. The Add-Pivot table is given the chart definition and told to create a tale + using the City field to create rows, the Product field to create columns and the data should be the sum of the gross field and the sum of the net field; + grand totals for both gross and net are included for rows (Cities) and columns (product) and the the data is explicitly formatted as a currency. + Not that in thee the chart definition the number format for the axis does not include any fraction part #> - param ( - #Name for the new Pivot table - this will be the name of a sheet in the workbook - [Parameter(Mandatory = $true)] - [string]$PivotTableName, - #By default a pivot table will be created on its own sheet, but it can be created on an existing sheet by giving the address where the top left corner of the table should go. (Allow two rows for the filter if one is used.) - [OfficeOpenXml.ExcelAddressBase] - $Address, - #An excel package object for the workbook. - $ExcelPackage, - #Worksheet where the data is found - $SourceWorkSheet, - #Address range in the worksheet e.g "A10:F20" - the first row must be column names: if not specified the whole sheet will be used. - $SourceRange, - #Fields to set as rows in the Pivot table - $PivotRows, - #A hash table in form "FieldName"="Function", where function is one of - #Average, Count, CountNums, Max, Min, Product, None, StdDev, StdDevP, Sum, Var, VarP - $PivotData, - #Fields to set as columns in the Pivot table - $PivotColumns, - #Fields to use to filter in the Pivot table - $PivotFilter, - [Switch]$PivotDataToColumn, - #By default Pivot tables have Totals for each Row (on the right) and for each column at the bottom. This allows just one or neither to be selected. - [ValidateSet("Both","Columns","Rows","None")] - [String]$PivotTotals = "Both", - #Included for compatibility - equivalent to -PivotTotals "None" - [Switch]$NoTotalsInPivot, - #Number format to apply to the data cells in the Pivot table - [string]$PivotNumberFormat, - #Apply a table style to the PivotTable - [OfficeOpenXml.Table.TableStyles]$PivotTableSyle, - #Use a chart definition instead of specifying chart settings one by one - [Parameter(ParameterSetName='ChartbyDef', Mandatory=$true, ValueFromPipelineByPropertyName=$true)] - $PivotChartDefinition, - #If specified a chart Will be included. - [Parameter(ParameterSetName='ChartbyParams')] - [Switch]$IncludePivotChart, - #Optional title for the pivot chart, by default the title omitted. - [Parameter(ParameterSetName='ChartbyParams')] - [String]$ChartTitle = "", - #Height of the chart in Pixels (400 by default) - [Parameter(ParameterSetName='ChartbyParams')] - [int]$ChartHeight = 400 , - #Width of the chart in Pixels (600 by default) - [Parameter(ParameterSetName='ChartbyParams')] - [int]$ChartWidth = 600, - #Cell position of the top left corner of the chart, there will be this number of rows above the top edge of the chart (default is 0, chart starts at top edge of row 1). - [Parameter(ParameterSetName='ChartbyParams')] - [Int]$ChartRow = 0 , - #Cell position of the top left corner of the chart, there will be this number of cells to the left of the chart (default is 4, chart starts at left edge of column E) - [Parameter(ParameterSetName='ChartbyParams')] - [Int]$ChartColumn = 4, - #Vertical offset of the chart from the cell corner. - [Parameter(ParameterSetName='ChartbyParams')] - [Int]$ChartRowOffSetPixels = 0 , - [Parameter(ParameterSetName='ChartbyParams')] - #Horizontal offset of the chart from the cell corner. - [Int]$ChartColumnOffSetPixels = 0, - #Type of chart - [Parameter(ParameterSetName='ChartbyParams')] - [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie', - #If specified hides the chart legend - [Parameter(ParameterSetName='ChartbyParams')] - [Switch]$NoLegend, - #if specified attaches the category to slices in a pie chart : not supported on all chart types, this may give errors if applied to an unsupported type. - [Parameter(ParameterSetName='ChartbyParams')] - [Switch]$ShowCategory, - #If specified attaches percentages to slices in a pie chart. - [Parameter(ParameterSetName='ChartbyParams')] - [Switch]$ShowPercent, - #If there is already content in the workbook the sheet with the Pivot table will not be active UNLESS Activate is specified - [switch]$Activate, - #Return the pivot table so it can be customized - [Switch]$PassThru - ) - if ($PivotTableName.length -gt 250) { - Write-warning -Message "Pivot table name will be truncated" - $PivotTableName = $PivotTableName.Substring(0,250) - } - if ($Address) { - [OfficeOpenXml.ExcelWorksheet]$wsPivot = $address.Worksheet - } - else { - [OfficeOpenXml.ExcelWorksheet]$wsPivot = Add-WorkSheet -ExcelPackage $ExcelPackage -WorksheetName $pivotTableName -Activate:$Activate - if ($wsPivot.Name -ne $PivotTableName) {Write-Warning -Message "The Worksheet name for the pivot table does not match the table name '$PivotTableName'; probably because excess or illegal characters were removed." } - if ($PivotFilter) {$Address = $wsPivot.Cells["A3"]} else { $Address = $wsPivot.Cells["A1"]} - } - #if the pivot doesn't exist, create it. - if (-not $wsPivot.PivotTables[$pivotTableName] ) { - try { - #Accept a string or a worksheet object as $Source Worksheet. - if ($SourceWorkSheet -is [string]) { - $SourceWorkSheet = $ExcelPackage.Workbook.Worksheets.where( {$_.name -Like $SourceWorkSheet})[0] - } - elseif ($SourceWorkSheet -is [int]) { - $SourceWorkSheet = $ExcelPackage.Workbook.Worksheets[$SourceWorkSheet] - } - if ($SourceWorkSheet -isnot [OfficeOpenXml.ExcelWorksheet]) {Write-Warning -Message "Could not find source Worksheet for pivot-table '$pivotTableName'." ; return } - else { - - if (-not $SourceRange) { $SourceRange = $SourceWorkSheet.Dimension.Address} - $pivotTable = $wsPivot.PivotTables.Add($Address, $SourceWorkSheet.Cells[ $SourceRange], $pivotTableName) - } - foreach ($Row in $PivotRows) { - try {$null = $pivotTable.RowFields.Add($pivotTable.Fields[$Row]) } - catch {Write-Warning -message "Could not add '$row' to Rows in PivotTable $pivotTableName." } - } - foreach ($Column in $PivotColumns) { - try {$null = $pivotTable.ColumnFields.Add($pivotTable.Fields[$Column])} - catch {Write-Warning -message "Could not add '$Column' to Columns in PivotTable $pivotTableName." } - } - if ($PivotData -is [HashTable] -or $PivotData -is [System.Collections.Specialized.OrderedDictionary]) { - $PivotData.Keys | ForEach-Object { - try { - $df = $pivotTable.DataFields.Add($pivotTable.Fields[$_]) - $df.Function = $PivotData.$_ - if ($PivotNumberFormat) {$df.Format = (Expand-NumberFormat -NumberFormat $PivotNumberFormat)} - } - catch {Write-Warning -message "Problem adding data fields to PivotTable $pivotTableName." } - } - } - else { - foreach ($field in $PivotData) { - try { - $df = $pivotTable.DataFields.Add($pivotTable.Fields[$field]) - $df.Function = 'Count' - } - catch {Write-Warning -message "Problem adding data field '$field' to PivotTable $pivotTableName." } - } - } - foreach ( $pFilter in $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 ($NoTotalsInPivot) {$PivotTotals = "None" } - if ($PivotTotals -eq "None" -or $PivotTotals -eq "Columns") { $pivotTable.RowGrandTotals = $false } - elseif ($PivotTotals -eq "Both" -or $PivotTotals -eq "Rows") { $pivotTable.RowGrandTotals = $true } - if ($PivotTotals -eq "None" -or $PivotTotals -eq "Rows") { $pivotTable.ColumGrandTotals = $false } # Epplus spelling mistake, not mine! - elseif ($PivotTotals -eq "Both" -or $PivotTotals -eq "Columns") { $pivotTable.ColumGrandTotals = $true } - if ($PivotDataToColumn ) { $pivotTable.DataOnRows = $false } - if ($PivotTableSyle) { $pivotTable.TableStyle = $PivotTableSyle} - } - catch {Write-Warning -Message "Failed adding PivotTable '$pivotTableName': $_"} - } - else { - Write-Warning -Message "Pivot table defined in $($pivotTableName) already exists, only the data range will be changed." - $pivotTable = $wsPivot.PivotTables[$pivotTableName] - $pivotTable.CacheDefinition.CacheDefinitionXml.pivotCacheDefinition.cacheSource.worksheetSource.ref = $SourceRange - } - - #Create the chart if it doesn't exist, leave alone if it does. - if ($IncludePivotChart -and -not $wsPivot.Drawings["Chart$pivotTableName"] ) { - try {Add-ExcelChart -PivotTable $pivotTable -ChartType $ChartType -Width $ChartWidth -Height $ChartHeight -Row $ChartRow -Column $ChartColumn -RowOffSetPixels $ChartRowOffSetPixels -ColumnOffSetPixels $ChartColumnOffSetPixels -Title $ChartTitle -NoLegend:$NoLegend -ShowCategory:$ShowCategory -ShowPercent:$ShowPercent } - catch {Write-Warning -Message "Failed adding chart for pivotable '$pivotTableName': $_"} - } - elseif ($PivotChartDefinition -and -not $wsPivot.Drawings["Chart$pivotTableName"]) { - $params = @{PivotTable= $pivotTable } - $PivotChartDefinition.PSObject.Properties | ForEach-Object {if ( $null -ne $_.value) {$params[$_.name] = $_.value}} - Add-ExcelChart @params - } - if ($PassThru) {return $pivotTable} + [cmdletbinding(defaultParameterSetName='ChartbyParams')] + [OutputType([OfficeOpenXml.Table.PivotTable.ExcelPivotTable])] + param ( + #Name for the new Pivot table - this will be the name of a sheet in the workbook + [Parameter(Mandatory = $true)] + [string]$PivotTableName, + #By default a pivot table will be created on its own sheet, but it can be created on an existing sheet by giving the address where the top left corner of the table should go. (Allow two rows for the filter if one is used.) + [OfficeOpenXml.ExcelAddressBase] + $Address, + #An excel package object for the workbook. + $ExcelPackage, + #Worksheet where the data is found + $SourceWorkSheet, + #Address range in the worksheet e.g "A10:F20" - the first row must be column names: if not specified the whole sheet will be used. + $SourceRange, + #Fields to set as rows in the Pivot table + $PivotRows, + #A hash table in form "FieldName"="Function", where function is one of + #Average, Count, CountNums, Max, Min, Product, None, StdDev, StdDevP, Sum, Var, VarP + $PivotData, + #Fields to set as columns in the Pivot table + $PivotColumns, + #Fields to use to filter in the Pivot table + $PivotFilter, + #By default Pivot tables have Totals for each Row (on the right) and for each column at the bottom. This allows just one or neither to be selected. + [Switch]$PivotDataToColumn, + #Define whther totals should be added to rows, columns neither, or both (the default is both) + [ValidateSet("Both","Columns","Rows","None")] + [String]$PivotTotals = "Both", + #Included for compatibility - equivalent to -PivotTotals "None" + [Switch]$NoTotalsInPivot, + #Number format to apply to the data cells in the Pivot table + [string]$PivotNumberFormat, + #Apply a table style to the PivotTable + [OfficeOpenXml.Table.TableStyles]$PivotTableSyle, + #Use a chart definition instead of specifying chart settings one by one + [Parameter(ParameterSetName='ChartbyDef', Mandatory=$true, ValueFromPipelineByPropertyName=$true)] + $PivotChartDefinition, + #If specified a chart Will be included. + [Parameter(ParameterSetName='ChartbyParams')] + [Switch]$IncludePivotChart, + #Optional title for the pivot chart, by default the title omitted. + [Parameter(ParameterSetName='ChartbyParams')] + [String]$ChartTitle = "", + #Height of the chart in Pixels (400 by default) + [Parameter(ParameterSetName='ChartbyParams')] + [int]$ChartHeight = 400 , + #Width of the chart in Pixels (600 by default) + [Parameter(ParameterSetName='ChartbyParams')] + [int]$ChartWidth = 600, + #Cell position of the top left corner of the chart, there will be this number of rows above the top edge of the chart (default is 0, chart starts at top edge of row 1). + [Parameter(ParameterSetName='ChartbyParams')] + [Int]$ChartRow = 0 , + #Cell position of the top left corner of the chart, there will be this number of cells to the left of the chart (default is 4, chart starts at left edge of column E) + [Parameter(ParameterSetName='ChartbyParams')] + [Int]$ChartColumn = 4, + #Vertical offset of the chart from the cell corner. + [Parameter(ParameterSetName='ChartbyParams')] + [Int]$ChartRowOffSetPixels = 0 , + [Parameter(ParameterSetName='ChartbyParams')] + #Horizontal offset of the chart from the cell corner. + [Int]$ChartColumnOffSetPixels = 0, + #Type of chart + [Parameter(ParameterSetName='ChartbyParams')] + [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie', + #If specified hides the chart legend + [Parameter(ParameterSetName='ChartbyParams')] + [Switch]$NoLegend, + #if specified attaches the category to slices in a pie chart : not supported on all chart types, this may give errors if applied to an unsupported type. + [Parameter(ParameterSetName='ChartbyParams')] + [Switch]$ShowCategory, + #If specified attaches percentages to slices in a pie chart. + [Parameter(ParameterSetName='ChartbyParams')] + [Switch]$ShowPercent, + #If there is already content in the workbook the sheet with the Pivot table will not be active UNLESS Activate is specified + [switch]$Activate, + #Return the pivot table so it can be customized + [Switch]$PassThru + ) + if ($PivotTableName.length -gt 250) { + Write-warning -Message "Pivot table name will be truncated" + $PivotTableName = $PivotTableName.Substring(0,250) } + if ($Address) { + [OfficeOpenXml.ExcelWorksheet]$wsPivot = $address.Worksheet + } + else { + [OfficeOpenXml.ExcelWorksheet]$wsPivot = Add-WorkSheet -ExcelPackage $ExcelPackage -WorksheetName $pivotTableName -Activate:$Activate + if ($wsPivot.Name -ne $PivotTableName) {Write-Warning -Message "The Worksheet name for the pivot table does not match the table name '$PivotTableName'; probably because excess or illegal characters were removed." } + if ($PivotFilter) {$Address = $wsPivot.Cells["A3"]} else { $Address = $wsPivot.Cells["A1"]} + } + #if the pivot doesn't exist, create it. + if (-not $wsPivot.PivotTables[$pivotTableName] ) { + try { + #Accept a string or a worksheet object as $Source Worksheet. + if ($SourceWorkSheet -is [string]) { + $SourceWorkSheet = $ExcelPackage.Workbook.Worksheets.where( {$_.name -Like $SourceWorkSheet})[0] + } + elseif ($SourceWorkSheet -is [int]) { + $SourceWorkSheet = $ExcelPackage.Workbook.Worksheets[$SourceWorkSheet] + } + if ($SourceWorkSheet -isnot [OfficeOpenXml.ExcelWorksheet]) {Write-Warning -Message "Could not find source Worksheet for pivot-table '$pivotTableName'." ; return } + else { + + if (-not $SourceRange) { $SourceRange = $SourceWorkSheet.Dimension.Address} + $pivotTable = $wsPivot.PivotTables.Add($Address, $SourceWorkSheet.Cells[ $SourceRange], $pivotTableName) + } + foreach ($Row in $PivotRows) { + try {$null = $pivotTable.RowFields.Add($pivotTable.Fields[$Row]) } + catch {Write-Warning -message "Could not add '$row' to Rows in PivotTable $pivotTableName." } + } + foreach ($Column in $PivotColumns) { + try {$null = $pivotTable.ColumnFields.Add($pivotTable.Fields[$Column])} + catch {Write-Warning -message "Could not add '$Column' to Columns in PivotTable $pivotTableName." } + } + if ($PivotData -is [HashTable] -or $PivotData -is [System.Collections.Specialized.OrderedDictionary]) { + $PivotData.Keys | ForEach-Object { + try { + $df = $pivotTable.DataFields.Add($pivotTable.Fields[$_]) + $df.Function = $PivotData.$_ + if ($PivotNumberFormat) {$df.Format = (Expand-NumberFormat -NumberFormat $PivotNumberFormat)} + } + catch {Write-Warning -message "Problem adding data fields to PivotTable $pivotTableName." } + } + } + else { + foreach ($field in $PivotData) { + try { + $df = $pivotTable.DataFields.Add($pivotTable.Fields[$field]) + $df.Function = 'Count' + } + catch {Write-Warning -message "Problem adding data field '$field' to PivotTable $pivotTableName." } + } + } + foreach ( $pFilter in $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 ($NoTotalsInPivot) {$PivotTotals = "None" } + if ($PivotTotals -eq "None" -or $PivotTotals -eq "Columns") { $pivotTable.RowGrandTotals = $false } + elseif ($PivotTotals -eq "Both" -or $PivotTotals -eq "Rows") { $pivotTable.RowGrandTotals = $true } + if ($PivotTotals -eq "None" -or $PivotTotals -eq "Rows") { $pivotTable.ColumGrandTotals = $false } # Epplus spelling mistake, not mine! + elseif ($PivotTotals -eq "Both" -or $PivotTotals -eq "Columns") { $pivotTable.ColumGrandTotals = $true } + if ($PivotDataToColumn ) { $pivotTable.DataOnRows = $false } + if ($PivotTableSyle) { $pivotTable.TableStyle = $PivotTableSyle} + } + catch {Write-Warning -Message "Failed adding PivotTable '$pivotTableName': $_"} + } + else { + Write-Warning -Message "Pivot table defined in $($pivotTableName) already exists, only the data range will be changed." + $pivotTable = $wsPivot.PivotTables[$pivotTableName] + if (-not $SourceRange) { $SourceRange = $SourceWorkSheet.Dimension.Address} + $pivotTable.CacheDefinition.CacheDefinitionXml.pivotCacheDefinition.cacheSource.worksheetSource.ref = $SourceRange + } + + #Create the chart if it doesn't exist, leave alone if it does. + if ($IncludePivotChart -and -not $wsPivot.Drawings["Chart$pivotTableName"] ) { + try {Add-ExcelChart -PivotTable $pivotTable -ChartType $ChartType -Width $ChartWidth -Height $ChartHeight -Row $ChartRow -Column $ChartColumn -RowOffSetPixels $ChartRowOffSetPixels -ColumnOffSetPixels $ChartColumnOffSetPixels -Title $ChartTitle -NoLegend:$NoLegend -ShowCategory:$ShowCategory -ShowPercent:$ShowPercent } + catch {Write-Warning -Message "Failed adding chart for pivotable '$pivotTableName': $_"} + } + elseif ($PivotChartDefinition -and -not $wsPivot.Drawings["Chart$pivotTableName"]) { + $params = @{PivotTable= $pivotTable } + $PivotChartDefinition.PSObject.Properties | ForEach-Object {if ( $null -ne $_.value) {$params[$_.name] = $_.value}} + Add-ExcelChart @params + } + if ($PassThru) {return $pivotTable} +} function New-PivotTableDefinition { - <# - .Synopsis - Creates Pivot table definitons for Export-Excel - .Description - Export-Excel allows a single Pivot table to be defined using the parameters -IncludePivotTable, -PivotColumns -PivotRows, - =PivotData, -PivotFilter, -PivotTotals, -PivotDataToColumn, -IncludePivotChart and -ChartType. - Its -PivotTableDefintion paramater allows multiple pivot tables to be defined, with additional parameters. - New-PivotTableDefinition is a convenient way to build these definitions. - .Example - $pt = New-PivotTableDefinition -PivotTableName "PT1" -SourceWorkSheet "Sheet1" -PivotRows "Status" -PivotData @{Status='Count' } -PivotFilter 'StartType' -IncludePivotChart -ChartType BarClustered3D - $Pt += New-PivotTableDefinition -PivotTableName "PT2" -SourceWorkSheet "Sheet2" -PivotRows "Company" -PivotData @{Company='Count'} -IncludePivotChart -ChartType PieExploded3D -ShowPercent -ChartTitle "Breakdown of processes by company" - Get-Service | Select-Object -Property Status,Name,DisplayName,StartType | Export-Excel -Path .\test.xlsx -AutoSize - Get-Process | Select-Object -Property Name,Company,Handles,CPU,VM | Export-Excel -Path .\test.xlsx -AutoSize -WorksheetName 'sheet2' - $excel = Export-Excel -Path .\test.xlsx -PivotTableDefinition $pt -Show + <# + .Synopsis + Creates Pivot table definitons for Export-Excel + .Description + Export-Excel allows a single Pivot table to be defined using the parameters -IncludePivotTable, -PivotColumns -PivotRows, + =PivotData, -PivotFilter, -PivotTotals, -PivotDataToColumn, -IncludePivotChart and -ChartType. + Its -PivotTableDefintion paramater allows multiple pivot tables to be defined, with additional parameters. + New-PivotTableDefinition is a convenient way to build these definitions. + .Example + $pt = New-PivotTableDefinition -PivotTableName "PT1" -SourceWorkSheet "Sheet1" -PivotRows "Status" -PivotData @{Status='Count' } -PivotFilter 'StartType' -IncludePivotChart -ChartType BarClustered3D + $Pt += New-PivotTableDefinition -PivotTableName "PT2" -SourceWorkSheet "Sheet2" -PivotRows "Company" -PivotData @{Company='Count'} -IncludePivotChart -ChartType PieExploded3D -ShowPercent -ChartTitle "Breakdown of processes by company" + Get-Service | Select-Object -Property Status,Name,DisplayName,StartType | Export-Excel -Path .\test.xlsx -AutoSize + Get-Process | Select-Object -Property Name,Company,Handles,CPU,VM | Export-Excel -Path .\test.xlsx -AutoSize -WorksheetName 'sheet2' + $excel = Export-Excel -Path .\test.xlsx -PivotTableDefinition $pt -Show - This is a re-work of one of the examples in Export-Excel - instead of writing out the pivot definition hash table it is built by calling New-PivotTableDefinition. - #> - param( - [Parameter(Mandatory)] - [Alias("PivtoTableName")]#Previous typo - use alias to avoid breaking scripts - $PivotTableName, - #Worksheet where the data is found - $SourceWorkSheet, - #Address range in the worksheet e.g "A10:F20" - the first row must be column names: if not specified the whole sheet will be used/ - $SourceRange, - #Fields to set as rows in the Pivot table - $PivotRows, - #A hash table in form "FieldName"="Function", where function is one of - #Average, Count, CountNums, Max, Min, Product, None, StdDev, StdDevP, Sum, Var, VarP - [hashtable]$PivotData, - #Fields to set as columns in the Pivot table - $PivotColumns, - #Fields to use to filter in the Pivot table - $PivotFilter, - [Switch]$PivotDataToColumn, - #By default Pivot tables have Totals for each Row (on the right) and for each column at the bottom. This allows just one or neither to be selected. - [ValidateSet("Both","Columns","Rows","None")] - [String]$PivotTotals = "Both", - #Included for compatibility - equivalent to -PivotTotals "None" - [Switch]$NoTotalsInPivot, - #Number format to apply to the data cells in the Pivot table - [string]$PivotNumberFormat, - #Apply a table style to the PivotTable - [OfficeOpenXml.Table.TableStyles]$PivotTableSyle, - #Use a chart definition instead of specifying chart settings one by one - [Parameter(ParameterSetName='ChartbyDef', Mandatory=$true, ValueFromPipelineByPropertyName=$true)] - $PivotChartDefinition, - #If specified a chart Will be included. - [Parameter(ParameterSetName='ChartbyParams')] - [Switch]$IncludePivotChart, - #Optional title for the pivot chart, by default the title omitted. - [Parameter(ParameterSetName='ChartbyParams')] - [String]$ChartTitle, - #Height of the chart in Pixels (400 by default) - [Parameter(ParameterSetName='ChartbyParams')] - [int]$ChartHeight = 400 , - #Width of the chart in Pixels (600 by default) - [Parameter(ParameterSetName='ChartbyParams')] - [int]$ChartWidth = 600, - #Cell position of the top left corner of the chart, there will be this number of rows above the top edge of the chart (default is 0, chart starts at top edge of row 1). - [Parameter(ParameterSetName='ChartbyParams')] - [Int]$ChartRow = 0 , - #Cell position of the top left corner of the chart, there will be this number of cells to the left of the chart (default is 4, chart starts at left edge of column E) - [Parameter(ParameterSetName='ChartbyParams')] - [Int]$ChartColumn = 4, - #Vertical offset of the chart from the cell corner. - [Parameter(ParameterSetName='ChartbyParams')] - [Int]$ChartRowOffSetPixels = 0 , - #Horizontal offset of the chart from the cell corner. - [Parameter(ParameterSetName='ChartbyParams')] - [Int]$ChartColumnOffSetPixels = 0, - #Type of chart - [Parameter(ParameterSetName='ChartbyParams')] - [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie', - #If specified hides the chart legend - [Parameter(ParameterSetName='ChartbyParams')] - [Switch]$NoLegend, - #if specified attaches the category to slices in a pie chart : not supported on all chart types, this may give errors if applied to an unsupported type. - [Parameter(ParameterSetName='ChartbyParams')] - [Switch]$ShowCategory, - #If specified attaches percentages to slices in a pie chart. - [Parameter(ParameterSetName='ChartbyParams')] - [Switch]$ShowPercent, - #If there is already content in the workbook the sheet with the Pivot table will not be active UNLESS Activate is specified - [switch]$Activate - ) - $validDataFuntions = [system.enum]::GetNames([OfficeOpenXml.Table.PivotTable.DataFieldFunctions]) + This is a re-work of one of the examples in Export-Excel - instead of writing out the pivot definition hash table it is built by calling New-PivotTableDefinition. + #> + param( + [Parameter(Mandatory)] + [Alias("PivtoTableName")]#Previous typo - use alias to avoid breaking scripts + $PivotTableName, + #Worksheet where the data is found + $SourceWorkSheet, + #Address range in the worksheet e.g "A10:F20" - the first row must be column names: if not specified the whole sheet will be used/ + $SourceRange, + #Fields to set as rows in the Pivot table + $PivotRows, + #A hash table in form "FieldName"="Function", where function is one of + #Average, Count, CountNums, Max, Min, Product, None, StdDev, StdDevP, Sum, Var, VarP + [hashtable]$PivotData, + #Fields to set as columns in the Pivot table + $PivotColumns, + #Fields to use to filter in the Pivot table + $PivotFilter, + [Switch]$PivotDataToColumn, + #By default Pivot tables have Totals for each Row (on the right) and for each column at the bottom. This allows just one or neither to be selected. + [ValidateSet("Both","Columns","Rows","None")] + [String]$PivotTotals = "Both", + #Included for compatibility - equivalent to -PivotTotals "None" + [Switch]$NoTotalsInPivot, + #Number format to apply to the data cells in the Pivot table + [string]$PivotNumberFormat, + #Apply a table style to the PivotTable + [OfficeOpenXml.Table.TableStyles]$PivotTableSyle, + #Use a chart definition instead of specifying chart settings one by one + [Parameter(ParameterSetName='ChartbyDef', Mandatory=$true, ValueFromPipelineByPropertyName=$true)] + $PivotChartDefinition, + #If specified a chart Will be included. + [Parameter(ParameterSetName='ChartbyParams')] + [Switch]$IncludePivotChart, + #Optional title for the pivot chart, by default the title omitted. + [Parameter(ParameterSetName='ChartbyParams')] + [String]$ChartTitle, + #Height of the chart in Pixels (400 by default) + [Parameter(ParameterSetName='ChartbyParams')] + [int]$ChartHeight = 400 , + #Width of the chart in Pixels (600 by default) + [Parameter(ParameterSetName='ChartbyParams')] + [int]$ChartWidth = 600, + #Cell position of the top left corner of the chart, there will be this number of rows above the top edge of the chart (default is 0, chart starts at top edge of row 1). + [Parameter(ParameterSetName='ChartbyParams')] + [Int]$ChartRow = 0 , + #Cell position of the top left corner of the chart, there will be this number of cells to the left of the chart (default is 4, chart starts at left edge of column E) + [Parameter(ParameterSetName='ChartbyParams')] + [Int]$ChartColumn = 4, + #Vertical offset of the chart from the cell corner. + [Parameter(ParameterSetName='ChartbyParams')] + [Int]$ChartRowOffSetPixels = 0 , + #Horizontal offset of the chart from the cell corner. + [Parameter(ParameterSetName='ChartbyParams')] + [Int]$ChartColumnOffSetPixels = 0, + #Type of chart + [Parameter(ParameterSetName='ChartbyParams')] + [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie', + #If specified hides the chart legend + [Parameter(ParameterSetName='ChartbyParams')] + [Switch]$NoLegend, + #if specified attaches the category to slices in a pie chart : not supported on all chart types, this may give errors if applied to an unsupported type. + [Parameter(ParameterSetName='ChartbyParams')] + [Switch]$ShowCategory, + #If specified attaches percentages to slices in a pie chart. + [Parameter(ParameterSetName='ChartbyParams')] + [Switch]$ShowPercent, + #If there is already content in the workbook the sheet with the Pivot table will not be active UNLESS Activate is specified + [switch]$Activate + ) + $validDataFuntions = [system.enum]::GetNames([OfficeOpenXml.Table.PivotTable.DataFieldFunctions]) - if ($PivotData.values.Where({$_ -notin $validDataFuntions}) ) { - Write-Warning -Message ("Pivot data functions might not be valid, they should be one of " + ($validDataFuntions -join ", ") + ".") - } - - $parameters = @{} + $PSBoundParameters - if ($NoTotalsInPivot) { - $parameters.Remove('NoTotalsInPivot') - $parameters["PivotTotals"] = "None" - } - $parameters.Remove('PivotTableName') - if ($PivotChartDefinition) { - $parameters.PivotChartDefinition.XRange = $null - $parameters.PivotChartDefinition.YRange = $null - $parameters.PivotChartDefinition.SeriesHeader = $null - } - @{$PivotTableName = $parameters} + if ($PivotData.values.Where({$_ -notin $validDataFuntions}) ) { + Write-Warning -Message ("Pivot data functions might not be valid, they should be one of " + ($validDataFuntions -join ", ") + ".") } + + $parameters = @{} + $PSBoundParameters + if ($NoTotalsInPivot) { + $parameters.Remove('NoTotalsInPivot') + $parameters["PivotTotals"] = "None" + } + $parameters.Remove('PivotTableName') + if ($PivotChartDefinition) { + $parameters.PivotChartDefinition.XRange = $null + $parameters.PivotChartDefinition.YRange = $null + $parameters.PivotChartDefinition.SeriesHeader = $null + } + @{$PivotTableName = $parameters} +} diff --git a/Send-SqlDataToExcel.ps1 b/Send-SqlDataToExcel.ps1 index 30eb821..a178e3a 100644 --- a/Send-SqlDataToExcel.ps1 +++ b/Send-SqlDataToExcel.ps1 @@ -52,6 +52,8 @@ Name(s) columns from the spreadhseet which will provide the Filter name(s) in a pivot table created from command line parameters. .PARAMETER PivotData In a pivot table created from command line parameters, the fields to use in the table body is given as a Hash table in the form ColumnName = Average|Count|CountNums|Max|Min|Product|None|StdDev|StdDevP|Sum|Var|VarP . + .PARAMETER PivotDataToColumn + If there are multiple datasets in a PivotTable, by default they are shown seperatate rows under the given row heading; this switch makes them seperate columns. .PARAMETER NoTotalsInPivot In a pivot table created from command line parameters, prevents the addition of totals to rows and columns. .PARAMETER IncludePivotChart @@ -111,6 +113,9 @@ Sizes the width of the Excel column to the maximum width needed to display all the containing data in that cell. .PARAMETER Show Opens the Excel file immediately after creation. Convenient for viewing the results instantly without having to search for the file first. + .PARAMETER CellStyleSB + A script block which is run at the end of the process to apply styles to cells (although it can be used for other purposes). + The script block is given three paramaters; an object containing the current worksheet, the Total number of Rows and the number of the last column. .PARAMETER ReturnRange If specified, Export-Excel returns the range of added cells in the format "A1:Z100" .PARAMETER PassThru diff --git a/Set-Column.ps1 b/Set-Column.ps1 index 7c461ee..cfb6007 100644 --- a/Set-Column.ps1 +++ b/Set-Column.ps1 @@ -7,20 +7,31 @@ which evaluates to a string, and optionally a column number and fills that value down the column. A column name can be specified and the new column can be made a named range. The column can be formatted. - .Example - C:> Set-Column -Worksheet $ws -Heading "WinsToFastLaps" -Value {"=E$row/C$row"} -Column 7 -AutoSize -AutoNameRange + .EXAMPLE + C:\>Set-Column -Worksheet $ws -Column 5 -NumberFormat 'Currency' + $ws contains a worksheet object - and column E is set to use the local currecy format. + Intelisense will complete predefined number formats. Expand-NumberFormat currency will show you how currency is interpreted on the local computer. + .EXAMPLE + C:\> Set-Column -Worksheet $ws -Heading "WinsToFastLaps" -Value {"=E$row/C$row"} -Column 7 -AutoSize -AutoNameRange Here $WS already contains a worksheet which contains counts of races won and fastest laps recorded by racing drivers (in columns C and E) Set-Column specifies that Column 7 should have a heading of "WinsToFastLaps" and the data cells should contain =E2/C2 , =E3/C3 the data cells should become a named range, which will also be "WinsToFastLaps" the column width will be set automatically + .EXAMPLE + C:\> Set-Column -Worksheet $ws -Heading "Link" -Value {"https://en.wikipedia.org" + $worksheet.cells["B$Row"].value } -AutoSize + In this example, the worksheet in $ws has partial links to wikipedia pages in column B. + Value is is a script block and it outputs a string which begins https... and ends with the value of cell at column B in the current row. + When given a valid URI set-Column makes it a hyperlink The column will be autosized to fit the links. #> [cmdletbinding()] Param ( + #If specifing the worksheet by name the ExcelPackage object which contains it needs to be passed [Parameter(ParameterSetName="Package",Mandatory=$true)] [OfficeOpenXml.ExcelPackage]$ExcelPackage, #The sheet to update can be a given as a name or an Excel Worksheet object - this sets it by name [Parameter(ParameterSetName="Package")] #The sheet to update can be a given as a name or an Excel Worksheet object - $workSheet contains the object [String]$Worksheetname = "Sheet1", + #The worksheet object can be passed instead of passing a sheet name and a package. [Parameter(ParameterSetName="sheet",Mandatory=$true)] [OfficeOpenXml.ExcelWorksheet]$Worksheet, #Column to fill down - first column is 1. 0 will be interpreted as first unused column @@ -98,12 +109,13 @@ if ($Column -eq 0 ) {$Column = $endColumn + 1 } $columnName = [OfficeOpenXml.ExcelCellAddress]::new(1,$column).Address -replace "1","" + Write-Verbose -Message "Updating Column $columnName" #If there is a heading, insert it and use it as the name for a range (if we're creating one) if ($Heading) { $Worksheet.Cells[$StartRow, $Column].Value = $Heading $StartRow ++ - if ($AutoNameRange) { $Worksheet.Names.Add( $Heading, ($Worksheet.Cells[$StartRow, $Column, $endRow, $Column]) ) | Out-Null } + if ($AutoNameRange) { Add-ExcelName -Range $Worksheet.Cells[$StartRow, $Column, $endRow, $Column] -RangeName $Heading } } #Fill in the data if ($PSBoundParameters.ContainsKey('Value')) { foreach ($row in ($StartRow..$endRow)) { diff --git a/Set-Row.ps1 b/Set-Row.ps1 index 4fd3f8a..3b67b16 100644 --- a/Set-Row.ps1 +++ b/Set-Row.ps1 @@ -54,6 +54,7 @@ [Switch]$StrikeThru, #Subscript or superscript (or none) [OfficeOpenXml.Style.ExcelVerticalAlignmentFont]$FontShift, + #Font to use - Excel defaults to Calibri [String]$FontName, #Point size for the text [float]$FontSize, diff --git a/SetFormat.ps1 b/SetFormat.ps1 index 5638fe3..1407a77 100644 --- a/SetFormat.ps1 +++ b/SetFormat.ps1 @@ -1,15 +1,14 @@ Function Set-Format { -<# -.SYNOPSIS - Applies Number, font, alignment and colour formatting to a range of Excel Cells -.EXAMPLE - $sheet.Column(3) | Set-Format -HorizontalAlignment Right -NumberFormat "#,###" - Selects column 3 from a sheet object (within a workbook object, which is a child of the ExcelPackage object) and passes it to Set-Format which formats as an integer with comma seperated groups -.EXAMPLE - Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NumberFormat "#,###" - Instead of piping the address in this version specifies a block of cells and applies similar formatting - -#> + <# + .SYNOPSIS + Applies Number, font, alignment and colour formatting to a range of Excel Cells + .EXAMPLE + $sheet.Column(3) | Set-Format -HorizontalAlignment Right -NumberFormat "#,###" + Selects column 3 from a sheet object (within a workbook object, which is a child of the ExcelPackage object) and passes it to Set-Format which formats as an integer with comma seperated groups + .EXAMPLE + Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NumberFormat "#,###" + Instead of piping the address in this version specifies a block of cells and applies similar formatting + #> Param ( #One or more row(s), Column(s) and/or block(s) of cells to format [Parameter(ValueFromPipeline = $true,ParameterSetName="Address",Mandatory=$True,Position=0)] @@ -25,10 +24,15 @@ $NumberFormat, #Style of border to draw around the range [OfficeOpenXml.Style.ExcelBorderStyle]$BorderAround, + #Color of the border [System.Drawing.Color]$BorderColor=[System.Drawing.Color]::Black, + #Style for the bottom border [OfficeOpenXml.Style.ExcelBorderStyle]$BorderBottom, + #Style for the top border [OfficeOpenXml.Style.ExcelBorderStyle]$BorderTop, + #Style for the left border [OfficeOpenXml.Style.ExcelBorderStyle]$BorderLeft, + #Style for the right border [OfficeOpenXml.Style.ExcelBorderStyle]$BorderRight, #Colour for the text - if none specified it will be left as it it is [System.Drawing.Color]$FontColor, @@ -82,7 +86,7 @@ #Hide a row or column (not a range); use -Hidden:$false to unhide [Switch]$Hidden ) - begin { + begin { #Allow Set-Format to take Worksheet and range parameters (like Add Contitional formatting) - convert them to an address if ($WorkSheet -and $Range) {$Address = $WorkSheet.Cells[$Range] } } @@ -267,7 +271,33 @@ if (Get-Command -ErrorAction SilentlyContinue -name Register-ArgumentCompleter) } Function Expand-NumberFormat { - param ($NumberFormat) + <# + .SYNOPSIS + Converts short names for Number formats to the formatting strings used in Excel + .DESCRIPTION + Where you can type a number format you can write, for example 'Short-Date' and the module will translate it into the format string used by excel + Some formats, like Short-Date change how they are presented when Excel loads. (So date will use the local ordering of year, month and Day) + Other formats change how they appear when loaded with different cultures (depending on the country "," or "." or " " may be the thousand seperator + although excel always stores it as ",") + .EXAMPLE + Expand-NumberFormat percentage + + Returns "0.00%" + .EXAMPLE + Expand-NumberFormat Currency + + Returns the currency format specified in the local regional settings. This may not be the same as Excel uses + The regional settings set the currency symbol and then whether it is before or after the number and seperated with a space or not; + for negative numbers the number by wrapped in parentheses or a - sign might appear before or after the number and symbol. + So this returns $#,##0.00;($#,##0.00) for English US, #,##0.00 €;€#,##0.00- for French. (Note some Eurozone countries write €1,23 and others 1,23€ ) + In French the decimal point will be rendered as a "," and the thousand seperator as a space. + #> + [cmdletbinding()] + [OutputType([String])] + param ( + #the format string to Expand + $NumberFormat + ) switch ($NumberFormat) { "Currency" { #https://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.currencynegativepattern(v=vs.110).aspx diff --git a/ToDo.md b/ToDo.md index 928c715..01f877b 100644 --- a/ToDo.md +++ b/ToDo.md @@ -1,11 +1,4 @@ -- [X] Add -PivotTableSource parameter to Add-ExcelChart. -- [X] Refactor Add-PivotTable to use Add-ExcelChart rather duplicating code. Add support for -ExcelChartDefinition support for all chart params ?? -- [X] Add -Passthru to Add-ExcelChart and Add-PivotTable -- [X] Add support for PivotTable number format, data field name and possibly sort. - -- [x] Improve checking of worksheet, pivot names, range names and table names -- [ ] Investigate regional support for number conversion & possible date conversion. Also investigate feasablity of preserving number format when converting string to number -- [ ] Add help text for parmaters which don't have it ( PivotDataToColumn , NoClobber and CellStyleSB ) in Export Excel, copy to Send-SQLDataToExcel -- [ ] Add help in ConvertToExcelXLSx.ps1, Get-HTMLTable.ps1, GetRange.PS1, GetExcelTable.Ps1, Import-HTML.PS1, New-ConditionalFormattingIconSet.Ps1, NewConditionalText.PS1, New-Psitem.PS1, Remove-Worksheet.ps1 and Add-ExcelChart - Copy parameter help from function Add-ExcelChart into New-ExcelChart.ps1 -- [ ] Add examples to add-ConditionalFormat, set-format,set-Row and Set-column (e.g. from tests) - [ ] Add Examples and tests for new "Quick charts" in Export Excel (replace examples) that use Charting.ps1, GetXYRange.ps1, InferData.PS1 (move these to deprecated). -- [ ] Increase Test code covereage for import-excel \ No newline at end of file +- [ ] Investigate regional support for number conversion & possible date conversion. Also investigate feasablity of preserving number format when converting string to number +- [ ] Add help to ConvertToExcelXLSx.ps1, Get-HTMLTable.ps1, GetRange.PS1, GetExcelTable.Ps1, Import-HTML.PS1, New-Psitem.PS1 and Remove-Worksheet.ps1 +- [ ] Increase Test code-covereage for import-excel \ No newline at end of file diff --git a/__tests__/First10Races.tests.ps1 b/__tests__/First10Races.tests.ps1 index 9cbd288..c9e2f64 100644 --- a/__tests__/First10Races.tests.ps1 +++ b/__tests__/First10Races.tests.ps1 @@ -18,7 +18,8 @@ 1..$columns | foreach {Add-ExcelName -Range $worksheet.cells[$topRow,$_,$lastDataRow,$_]} - Set-Column -Worksheet $worksheet -StartRow $topRow -Heading "PlacesGained/Lost" -Value "=GridPostion-FinishPosition" -AutoNameRange + $scwarnVar = $null + Set-Column -Worksheet $worksheet -StartRow $topRow -Heading "PlacesGained/Lost" -Value "=GridPostion-FinishPosition" -AutoNameRange -WarningVariable scWarnVar -WarningAction SilentlyContinue $columns ++ #create a table which covers all the data. And define a pivot table which uses the same address range. @@ -30,11 +31,12 @@ $cf.Icon2.Value = 0 $cf.Icon3.Value = 1 Add-ConditionalFormatting -Address $worksheet.cells["FinishPosition"] -RuleType Equal -ConditionValue 1 -ForeGroundColor Purple -Bold -Priority 1 -StopIfTrue - Add-ConditionalFormatting -Address $worksheet.cells["FinishPosition"] -RuleType LessThanOrEqual -ConditionValue 3 -Bold -Priority 2 -StopIfTrue - + + $ct = New-ConditionalText -Text "Ferrari" + $ct2 = New-ConditionalText -Range $worksheet.Names["FinishPosition"].Address -ConditionalType LessThanOrEqual -Text 3 -ConditionalTextColor Red -BackgroundColor White #Create links for each group name (race) and Export them so they start at Cell A1; create a pivot table with definition just created, save the file and open in Excel $results | foreach {(New-Object -TypeName OfficeOpenXml.ExcelHyperLink -ArgumentList "Sheet1!$($_.Name)" , "$($_.name) GP")} | - Export-Excel -ExcelPackage $excel -AutoSize -PivotTableDefinition $pt -Calculate + Export-Excel -ExcelPackage $excel -AutoSize -PivotTableDefinition $pt -Calculate -Conditionaltext $ct,$ct2 $excel = Open-ExcelPackage $path $sheet = $excel.Workbook.Worksheets[1] @@ -59,11 +61,11 @@ $sheet.Cells[( $results.Count),$columns] | Should benullorEmpty $sheet.Cells[(1+$results.Count),$columns].Value | Should be "PlacesGained/Lost" $sheet.Cells[(2+$results.Count),$columns].Formula | should be "GridPostion-FinishPosition" - $sheet.Names["PlacesGained/Lost"] | should not benullorEmpty + $sheet.Names["PlacesGained_Lost"] | should not benullorEmpty } It "Performed the calculation " { $placesMade = $Sheet.Cells[(2+$results.Count),5].value - $Sheet.Cells[(2+$results.Count),3].value - $sheet.Cells[(2+$results.Count),$columns].value | Should be $placesmade + $sheet.Cells[(2+$results.Count),$columns].value | Should be $placesmade } It "Applied ConditionalFormatting, including stopifTrue and Priority " { $sheet.ConditionalFormatting[0].Address.Start.Column | should be $columns @@ -72,9 +74,7 @@ $sheet.ConditionalFormatting[0].Address.Start.Row | should be ($results.Count + 1) $sheet.ConditionalFormatting[0].Icon3.Type.ToString() | Should be "Num" $sheet.ConditionalFormatting[0].Icon3.Value | Should be 1 - $sheet.ConditionalFormatting[0].Priority | Should be 3 $sheet.ConditionalFormatting[1].Priority | Should be 1 - $sheet.ConditionalFormatting[2].Priority | Should be 2 $sheet.ConditionalFormatting[1].StopIfTrue | Should be $true } }