diff --git a/AddConditionalFormatting.ps1 b/AddConditionalFormatting.ps1 index 076933f..be10f08 100644 --- a/AddConditionalFormatting.ps1 +++ b/AddConditionalFormatting.ps1 @@ -2,9 +2,17 @@ <# .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 - + > + PS> $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 @@ -13,136 +21,194 @@ 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 + > + >PS $r = Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Range "B1:B100" -ThreeIconsSet Flags -Passthru + $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 + 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 + 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 - [Parameter(Mandatory = $true, ParameterSetName = "NamedRule")] - [Parameter(Mandatory = $true, ParameterSetName = "DataBar")] - [Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSet")] - [Parameter(Mandatory = $true, ParameterSetName = "FourIconSet")] - [Parameter(Mandatory = $true, ParameterSetName = "FiveIconSet")] - [OfficeOpenXml.ExcelWorksheet]$WorkSheet , - #The area of the worksheet where the format is to be applied - [Parameter(Mandatory = $true, ParameterSetName = "NamedRule")] - [Parameter(Mandatory = $true, ParameterSetName = "DataBar")] - [Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSet")] - [Parameter(Mandatory = $true, ParameterSetName = "FourIconSet")] - [Parameter(Mandatory = $true, ParameterSetName = "FiveIconSet")] - [OfficeOpenXml.ExcelAddress]$Range , - #One or more row(s), column(s) and/or block(s) of cells to format - [Parameter(Mandatory = $true, ParameterSetName = "NamedRuleAddress")] - [Parameter(Mandatory = $true, ParameterSetName = "DataBarAddress")] - [Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSetAddress")] - [Parameter(Mandatory = $true, ParameterSetName = "FourIconSetAddress")] - [Parameter(Mandatory = $true, ParameterSetName = "FiveIconSetAddress")] + #A block of cells to format - you can use a named range with -Address $ws.names[1] or $ws.cells["RangeName"] + [Parameter(Mandatory = $true, Position = 0)] + [Alias("Range")] $Address , + #The worksheet where the format is to be applied + [OfficeOpenXml.ExcelWorksheet]$WorkSheet , #One of the standard named rules - Top / Bottom / Less than / Greater than / Contains etc. - [Parameter(Mandatory = $true, ParameterSetName = "NamedRule", Position = 3)] - [Parameter(Mandatory = $true, ParameterSetName = "NamedRuleAddress", Position = 3)] + [Parameter(Mandatory = $true, ParameterSetName = "NamedRule", Position = 1)] [OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType]$RuleType , #Text colour for matching objects + [Parameter(ParameterSetName = "NamedRule")] [Alias("ForeGroundColour")] [System.Drawing.Color]$ForeGroundColor, #colour for databar type charts [Parameter(Mandatory = $true, ParameterSetName = "DataBar")] - [Parameter(Mandatory = $true, ParameterSetName = "DataBarAddress")] [Alias("DataBarColour")] [System.Drawing.Color]$DataBarColor, #One of the three-icon set types (e.g. Traffic Lights) [Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSet")] - [Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSetAddress")] [OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting3IconsSetType]$ThreeIconsSet, #A four-icon set name [Parameter(Mandatory = $true, ParameterSetName = "FourIconSet")] - [Parameter(Mandatory = $true, ParameterSetName = "FourIconSetAddress")] [OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting4IconsSetType]$FourIconsSet, #A five-icon set name [Parameter(Mandatory = $true, ParameterSetName = "FiveIconSet")] - [Parameter(Mandatory = $true, ParameterSetName = "FiveIconSetAddress")] [OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting5IconsSetType]$FiveIconsSet, - #Use the icon set in reverse order + #Use the icon set in reverse order, or reverse the orders of Two- & Three-Color Scales + [Parameter(ParameterSetName = "NamedRule")] [Parameter(ParameterSetName = "ThreeIconSet")] - [Parameter(ParameterSetName = "ThreeIconSetAddress")] [Parameter(ParameterSetName = "FourIconSet")] - [Parameter(ParameterSetName = "FourIconSetAddress")] [Parameter(ParameterSetName = "FiveIconSet")] - [Parameter(ParameterSetName = "FiveIconSetAddress")] [switch]$Reverse, - #A value for the condition (e.g. "2000" if the test is 'lessthan 2000') - [string]$ConditionValue, + #A value for the condition (e.g. 2000 if the test is 'lessthan 2000' ; Formulas should begin with "=" ) + [Parameter(ParameterSetName = "NamedRule")] + $ConditionValue, #A second value for the conditions like "between x and Y" - [string]$ConditionValue2, + [Parameter(ParameterSetName = "NamedRule")] + $ConditionValue2, #Background colour for matching items + [Parameter(ParameterSetName = "NamedRule")] [System.Drawing.Color]$BackgroundColor, #Background pattern for matching items + [Parameter(ParameterSetName = "NamedRule")] [OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::None , #Secondary colour when a background pattern requires it + [Parameter(ParameterSetName = "NamedRule")] [System.Drawing.Color]$PatternColor, #Sets the numeric format for matching items + [Parameter(ParameterSetName = "NamedRule")] $NumberFormat, #Put matching items in bold face + [Parameter(ParameterSetName = "NamedRule")] [switch]$Bold, #Put matching items in italic + [Parameter(ParameterSetName = "NamedRule")] [switch]$Italic, #Underline matching items + [Parameter(ParameterSetName = "NamedRule")] [switch]$Underline, #Strikethrough text of matching items + [Parameter(ParameterSetName = "NamedRule")] [switch]$StrikeThru, + #Prevent the processing of subsequent rules + [Parameter(ParameterSetName = "NamedRule")] + [switch]$StopIfTrue, + #Set the sequence for rule processong + [int]$Priority, #If specified pass the rule back to the caller to allow additional customization. [switch]$Passthru ) - - #Allow conditional formatting to work like Set-Format (with single ADDRESS parameter), split it to get worksheet and range of cells. - If ($Address -and -not $WorkSheet -and -not $Range) { - $WorkSheet = $Address.Worksheet[0] - $Range = $Address.Address + + #Allow conditional formatting to work like Set-ExcelRange (with single ADDRESS parameter), split it to get worksheet and range of cells. + If ($Address -is [OfficeOpenXml.Table.ExcelTable]) { + $WorkSheet = $Address.Address.Worksheet + $Address = $Address.Address.Address + } + elseif ($Address.Address -and $Address.Worksheet -and -not $WorkSheet) { #Address is a rangebase or similar + $WorkSheet = $Address.Worksheet[0] + $Address = $Address.Address + } + elseif ($Address -is [String] -and $WorkSheet -and $WorkSheet.Names[$Address] ) { #Address is the name of a named range. + $Address = $WorkSheet.Names[$Address].Address + } + if (($Address -is [OfficeOpenXml.ExcelRow] -and -not $WorkSheet) -or + ($Address -is [OfficeOpenXml.ExcelColumn] -and -not $WorkSheet) ){ #EPPLUs Can't get the worksheet object from a row or column object, so bail if that was tried + Write-Warning -Message "Add-ConditionalFormatting does not support Row or Column objects as an address; use a worksheet and/or specify 'R:R' or 'C:C' instead. "; return + } + elseif ($Address -is [OfficeOpenXml.ExcelRow]) { #But if we have a column or row object and a worksheet (I don't know *why*) turn them into a string for the range + $Address = "$($Address.Row):$($Address.Row)" + } + elseif ($Address -is [OfficeOpenXml.ExcelColumn]) { + $Address = [OfficeOpenXml.ExcelAddress]::new(1,$address.ColumnMin,1,$address.ColumnMax).Address -replace '1','' + if ($Address -notmatch ':') {$Address = "$Address`:$Address"} + } + if ( $Address -is [string] -and $Address -match "!") {$Address = $Address -replace '^.*!',''} + #By this point we should have a worksheet object whose ConditionalFormatting collection we will add to. If not, bail. + if (-not $worksheet -or $WorkSheet -isnot [OfficeOpenXml.ExcelWorksheet]) {write-warning "You need to provide a worksheet object." ; return} + #region create a rule of the right type + if ($RuleType -match 'IconSet$') {Write-warning -Message "You cannot configure a IconSet rule in this way; please use -$RuleType ." ; return} + if ($PSBoundParameters.ContainsKey("ThreeIconsSet" ) ) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Address , $ThreeIconsSet)} + elseif ($PSBoundParameters.ContainsKey("FourIconsSet" ) ) {$rule = $WorkSheet.ConditionalFormatting.AddFourIconSet( $Address , $FourIconsSet )} + elseif ($PSBoundParameters.ContainsKey("FiveIconsSet" ) ) {$rule = $WorkSheet.ConditionalFormatting.AddFiveIconSet( $Address , $FiveIconsSet )} + elseif ($PSBoundParameters.ContainsKey("DataBarColor" ) ) {$rule = $WorkSheet.ConditionalFormatting.AddDatabar( $Address , $DataBarColor )} + else {$rule = ($WorkSheet.ConditionalFormatting)."Add$RuleType"($Address ) } + if ($Reverse) { + if ($rule.type -match 'IconSet$' ) {$rule.reverse = $true} + elseif ($rule.type -match 'ColorScale$') {$temp =$rule.LowValue.Color ; $rule.LowValue.Color = $rule.HighValue.Color; $rule.HighValue.Color = $temp} + else {Write-Warning -Message "-Reverse was ignored because $ruletype does not support it."} } - #region Create a rule of the right type - if ($PSBoundParameters.ContainsKey("ThreeIconsSet" ) ) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Range , $ThreeIconsSet)} - elseif ($PSBoundParameters.ContainsKey("FourIconsSet" ) ) {$rule = $WorkSheet.ConditionalFormatting.AddFourIconSet( $Range , $FourIconsSet) } - elseif ($PSBoundParameters.ContainsKey("FiveIconsSet" ) ) {$rule = $WorkSheet.ConditionalFormatting.AddFiveIconSet( $Range , $FiveIconsSet) } - elseif ($PSBoundParameters.ContainsKey("DataBarColor" ) ) {$rule = $WorkSheet.ConditionalFormatting.AddDatabar( $Range , $DataBarColor) } - else {$rule = ($WorkSheet.ConditionalFormatting)."Add$RuleType"($Range)} - if ($PSBoundParameters.ContainsKey("Reverse" ) ) {$rule.reverse = [boolean]$Reverse} #endregion #region set the rule conditions - if ($PSBoundParameters.ContainsKey("ConditionValue") -and - $RuleType -match "Top|Botom" ) {$rule.Rank = $ConditionValue } - if ($PSBoundParameters.ContainsKey("ConditionValue") -and - $RuleType -match "StdDev" ) {$rule.StdDev = $ConditionValue } - if ($PSBoundParameters.ContainsKey("ConditionValue") -and - $RuleType -match "Than|Equal|Expression" ) {$rule.Formula = ($ConditionValue -replace '^=','') } - if ($PSBoundParameters.ContainsKey("ConditionValue") -and - $RuleType -match "Text|With" ) {$rule.Text = ($ConditionValue -replace '^=','') } - if ($PSBoundParameters.ContainsKey("ConditionValue") -and - $PSBoundParameters.ContainsKey("ConditionValue") -and - $RuleType -match "Between" ) { - $rule.Formula = ($ConditionValue -replace '^=',''); - $rule.Formula2 = ($ConditionValue2 -replace '^=','') + #for lessThan/GreaterThan/Equal/Between conditions make sure that strings are wrapped in quotes. Formulas should be passed with = which will be stripped. + if ($RuleType -match "Than|Equal|Between" ) { + if ($ConditionValue) { + $number = $Null + #if the condition type is not a value type, but parses as a number, make it the number + if ($ConditionValue -isnot [System.ValueType] -and [Double]::TryParse($ConditionValue, [System.Globalization.NumberStyles]::Any, [System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number) ) { + $ConditionValue = $number + } #else if it is not a value type, or a formula, or wrapped in quotes, wrap it in quotes. + elseif (($ConditionValue -isnot [System.ValueType])-and ($ConditionValue -notmatch '^=') -and ($ConditionValue -notmatch '^".*"$') ) { + $ConditionValue = '"' + $ConditionValue +'"' + } + } + if ($ConditionValue2) { + $number = $Null + if ($ConditionValue -isnot [System.ValueType] -and [Double]::TryParse($ConditionValue2, [System.Globalization.NumberStyles]::Any, [System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number) ) { + $ConditionValue2 = $number + } + elseif (($ConditionValue -isnot [System.ValueType]) -and ($ConditionValue2 -notmatch '^=') -and ($ConditionValue2 -notmatch '^".*"$') ) { + $ConditionValue2 = '"' + $ConditionValue2 + '"' + } + } } + #But we don't usually want quotes round containstext | beginswith type rules. Can't be Certain they need to be removed, so warn the user their condition might be wrong + if ($RuleType -match "Text|With" -and $ConditionValue -match '^".*"$' ) { + Write-Warning -Message "The condition will look for the quotes at the start and end." + } + if ($PSBoundParameters.ContainsKey("ConditionValue" ) -and + $RuleType -match "Top|Botom" ) {$rule.Rank = $ConditionValue } + if ($PSBoundParameters.ContainsKey("ConditionValue" ) -and + $RuleType -match "StdDev" ) {$rule.StdDev = $ConditionValue } + if ($PSBoundParameters.ContainsKey("ConditionValue" ) -and + $RuleType -match "Than|Equal|Expression" ) {$rule.Formula = ($ConditionValue -replace '^=','') } + if ($PSBoundParameters.ContainsKey("ConditionValue" ) -and + $RuleType -match "Text|With" ) {$rule.Text = ($ConditionValue -replace '^=','') } + if ($PSBoundParameters.ContainsKey("ConditionValue" ) -and + $PSBoundParameters.ContainsKey("ConditionValue2") -and + $RuleType -match "Between" ) { + $rule.Formula = ($ConditionValue -replace '^=',''); + $rule.Formula2 = ($ConditionValue2 -replace '^=','') + } + if ($PSBoundParameters.ContainsKey("StopIfTrue") ) {$rule.StopIfTrue = $StopIfTrue } + if ($PSBoundParameters.ContainsKey("Priority") ) {$rule.Priority = $Priority } #endregion - #region set the rule format - if ($PSBoundParameters.ContainsKey("NumberFormat" ) ) {$rule.Style.NumberFormat.Format = (Expand-NumberFormat $NumberFormat) } + #region set the rule format + if ($PSBoundParameters.ContainsKey("NumberFormat" ) ) {$rule.Style.NumberFormat.Format = (Expand-NumberFormat $NumberFormat) } if ($Underline ) {$rule.Style.Font.Underline = [OfficeOpenXml.Style.ExcelUnderLineType]::Single } - elseif ($PSBoundParameters.ContainsKey("Underline" ) ) {$rule.Style.Font.Underline = [OfficeOpenXml.Style.ExcelUnderLineType]::None } - if ($PSBoundParameters.ContainsKey("Bold" ) ) {$rule.Style.Font.Bold = [boolean]$Bold } - if ($PSBoundParameters.ContainsKey("Italic" ) ) {$rule.Style.Font.Italic = [boolean]$Italic } - if ($PSBoundParameters.ContainsKey("StrikeThru") ) {$rule.Style.Font.Strike = [boolean]$StrikeThru } + elseif ($PSBoundParameters.ContainsKey("Underline" ) ) {$rule.Style.Font.Underline = [OfficeOpenXml.Style.ExcelUnderLineType]::None } + if ($PSBoundParameters.ContainsKey("Bold" ) ) {$rule.Style.Font.Bold = [boolean]$Bold } + if ($PSBoundParameters.ContainsKey("Italic" ) ) {$rule.Style.Font.Italic = [boolean]$Italic } + if ($PSBoundParameters.ContainsKey("StrikeThru" ) ) {$rule.Style.Font.Strike = [boolean]$StrikeThru } if ($PSBoundParameters.ContainsKey("ForeGroundColor" ) ) {$rule.Style.Font.Color.color = $ForeGroundColor } if ($PSBoundParameters.ContainsKey("BackgroundColor" ) ) {$rule.Style.Fill.BackgroundColor.color = $BackgroundColor } if ($PSBoundParameters.ContainsKey("BackgroundPattern") ) {$rule.Style.Fill.PatternType = $BackgroundPattern } if ($PSBoundParameters.ContainsKey("PatternColor" ) ) {$rule.Style.Fill.PatternColor.color = $PatternColor } - #endregion - #Allow further tweaking by returning the rule, if passthru specified + #endregion + #Allow further tweaking by returning the rule, if passthru specified if ($Passthru) {$rule} } \ No newline at end of file diff --git a/ColorCompletion.ps1 b/ColorCompletion.ps1 index 22ee985..ece2fde 100644 --- a/ColorCompletion.ps1 +++ b/ColorCompletion.ps1 @@ -25,14 +25,15 @@ if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue) Register-ArgumentCompleter -CommandName Merge-MulipleSheets -ParameterName KeyFontColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName New-ConditionalText -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion Register-ArgumentCompleter -CommandName New-ConditionalText -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName BorderColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Column -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Column -ParameterName FontColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Column -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Row -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Row -ParameterName FontColor -ScriptBlock $Function:ColorCompletion - Register-ArgumentCompleter -CommandName Set-Row -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName New-ConditionalText -ParameterName ConditionalTextColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-ExcelRange -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-ExcelRange -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-ExcelRange -ParameterName BorderColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-ExcelRange -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-ExcelColumn -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-ExcelColumn -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-ExcelColumn -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-ExcelRow -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-ExcelRow -ParameterName FontColor -ScriptBlock $Function:ColorCompletion + Register-ArgumentCompleter -CommandName Set-ExcelRow -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion } \ No newline at end of file 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/AddWorkSheet/AddWorkSheet.ps1 b/Examples/AddWorkSheet/AddWorkSheet.ps1 index a9120d0..4535d4c 100644 --- a/Examples/AddWorkSheet/AddWorkSheet.ps1 +++ b/Examples/AddWorkSheet/AddWorkSheet.ps1 @@ -4,8 +4,9 @@ $xlSourcefile = "$env:TEMP\Source.xlsx" Remove-Item $xlSourcefile -ErrorAction Ignore +#Put some simple data in a worksheet and Get an excel package object to represent the file $excel = 1..10 | Export-Excel $xlSourcefile -PassThru - +#Add a new worksheet named 'NewSheet' and copying the sheet that was just made (Sheet1) to the new sheet Add-WorkSheet -ExcelPackage $excel -WorkSheetname "NewSheet" -CopySource $excel.Workbook.Worksheets["Sheet1"] - +#Save and open in Excel Close-ExcelPackage -ExcelPackage $excel -Show 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/Examples/ConditionalFormatting/ConditionalText.ps1 b/Examples/ConditionalFormatting/ConditionalText.ps1 index c04c297..96130e1 100644 --- a/Examples/ConditionalFormatting/ConditionalText.ps1 +++ b/Examples/ConditionalFormatting/ConditionalText.ps1 @@ -1,13 +1,13 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -$file = ".\conditionalTextFormatting.xlsx" +$file = "$env:temp\conditionalTextFormatting.xlsx" Remove-Item $file -ErrorAction Ignore Get-Service | Select-Object Status, Name, DisplayName, ServiceName | Export-Excel $file -Show -AutoSize -AutoFilter -ConditionalText $( - New-ConditionalText stop - New-ConditionalText runn darkblue cyan - New-ConditionalText -ConditionalType EndsWith svc wheat green - New-ConditionalText -ConditionalType BeginsWith windows darkgreen wheat + New-ConditionalText stop #Stop is the condition value, the rule is defaults to 'Contains text' and the default Colors are used + New-ConditionalText runn darkblue cyan #runn is the condition value, the rule is defaults to 'Contains text'; the foregroundColur is darkblue and the background is cyan + New-ConditionalText -ConditionalType EndsWith svc wheat green #the rule here is 'Ends with' and the value is 'svc' the forground is wheat and the background dark green + New-ConditionalText -ConditionalType BeginsWith windows darkgreen wheat #this is 'Begins with "Windows"' the forground is dark green and the background wheat ) \ No newline at end of file diff --git a/Examples/ConditionalFormatting/ContainsBlanks.ps1 b/Examples/ConditionalFormatting/ContainsBlanks.ps1 index 83e01a4..45eb134 100644 --- a/Examples/ConditionalFormatting/ContainsBlanks.ps1 +++ b/Examples/ConditionalFormatting/ContainsBlanks.ps1 @@ -1,5 +1,6 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} +#Define a "Contains blanks" rule. No format is specified so it default to dark-red text on light-pink background. $ContainsBlanks = New-ConditionalText -ConditionalType ContainsBlanks $data = $( @@ -11,7 +12,8 @@ $data = $( New-PSItem g h i ) -$file ="c:\temp\testblanks.xlsx" +$file ="$env:temp\testblanks.xlsx" Remove-Item $file -ErrorAction Ignore +#use the conditional format definition created above $data | Export-Excel $file -show -ConditionalText $ContainsBlanks \ No newline at end of file diff --git a/Examples/ConditionalFormatting/Databar.ps1 b/Examples/ConditionalFormatting/Databar.ps1 index 9a87f89..3b72742 100644 --- a/Examples/ConditionalFormatting/Databar.ps1 +++ b/Examples/ConditionalFormatting/Databar.ps1 @@ -1,23 +1,32 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -Remove-Item -Path .\test.xlsx -ErrorAction Ignore +$path = "$env:temp\test.xlsx" +Remove-Item -Path $path -ErrorAction Ignore +#Export processes, and get an ExcelPackage object representing the file. $excel = Get-Process | Select-Object -Property Name,Company,Handles,CPU,PM,NPM,WS | - Export-Excel -Path .\test.xlsx -ClearSheet -WorkSheetname "Processes" -PassThru + Export-Excel -Path $path -ClearSheet -WorkSheetname "Processes" -PassThru $sheet = $excel.Workbook.Worksheets["Processes"] -$sheet.Column(1) | Set-Format -Bold -AutoFit + +#Apply fixed formatting to columns. Set-Format is an Alias for Set-Excel Range, -NFormat is an alias for numberformat +$sheet.Column(1) | Set-ExcelRange -Bold -AutoFit $sheet.Column(2) | Set-Format -Width 29 -WrapText $sheet.Column(3) | Set-Format -HorizontalAlignment Right -NFormat "#,###" -Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NFormat "#,###" -Set-Format -Address $sheet.Column(4) -HorizontalAlignment Right -NFormat "#,##0.0" -Bold +Set-ExcelRange -Range -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NFormat "#,###" +#Set-Format is an alias for Set-ExcelRange +Set-Format -Range $sheet.Column(4) -HorizontalAlignment Right -NFormat "#,##0.0" -Bold +#In Set-ExcelRange / Set-Format "-Address" is an alias for "-Range" Set-Format -Address $sheet.Row(1) -Bold -HorizontalAlignment Center -Add-ConditionalFormatting -WorkSheet $sheet -Range "D2:D1048576" -DataBarColor Red -Add-ConditionalFormatting -WorkSheet $sheet -Range "G2:G1048576" -RuleType GreaterThan -ConditionValue "104857600" -ForeGroundColor Red +#Create a Red Data-bar for the values in Column D +Add-ConditionalFormatting -WorkSheet $sheet -Address "D2:D1048576" -DataBarColor Red +# Conditional formatting applies to "Addreses" aliases allow either "Range" or "Address" to be used in Set-ExcelRange or Add-Conditional formatting. +Add-ConditionalFormatting -WorkSheet $sheet -Range "G2:G1048576" -RuleType GreaterThan -ConditionValue "104857600" -ForeGroundColor Red foreach ($c in 5..9) {Set-Format -Address $sheet.Column($c) -AutoFit } +#Create a pivot and save the file. Export-Excel -ExcelPackage $excel -WorkSheetname "Processes" -IncludePivotChart -ChartType ColumnClustered -NoLegend -PivotRows company -PivotData @{'Name'='Count'} -Show \ No newline at end of file diff --git a/Examples/ConditionalFormatting/FormatCalculations.ps1 b/Examples/ConditionalFormatting/FormatCalculations.ps1 index f2d271b..11098d7 100644 --- a/Examples/ConditionalFormatting/FormatCalculations.ps1 +++ b/Examples/ConditionalFormatting/FormatCalculations.ps1 @@ -1,6 +1,6 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -$f = ".\testExport.xlsx" +$f = "$env:TEMP\testExport.xlsx" Remove-Item $f -ErrorAction Ignore @@ -21,6 +21,8 @@ $data = $( New-PSItem Westerly 120 New-PSItem SouthWest 118 ) +# in this example instead of doing $variable = New-Conditional text .... ; Export-excel -conditionalText $variable +# the syntax is used is Export-excel -conditionalText (New-Conditional text ) #$data | Export-Excel $f -Show -AutoSize -ConditionalText (New-ConditionalText -ConditionalType AboveAverage) diff --git a/Examples/ConditionalFormatting/GetProcess.ps1 b/Examples/ConditionalFormatting/GetProcess.ps1 index 773c490..ed6c47e 100644 --- a/Examples/ConditionalFormatting/GetProcess.ps1 +++ b/Examples/ConditionalFormatting/GetProcess.ps1 @@ -1,10 +1,12 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -Remove-Item .\testExport.xlsx -ErrorAction Ignore +Remove-Item "$env:TEMP\testExport.xlsx" -ErrorAction Ignore Get-Process | Where-Object Company | Select-Object Company, Name, PM, Handles, *mem* | - Export-Excel .\testExport.xlsx -Show -AutoSize -AutoNameRange ` +#This example creates a 3 Icon set for the values in the "PM column, and Highlights company names (anywhere in the data) with different colors + + Export-Excel "$env:TEMP\testExport.xlsx" -Show -AutoSize -AutoNameRange ` -ConditionalFormat $( New-ConditionalFormattingIconSet -Range "C:C" ` -ConditionalFormat ThreeIconSet -IconType Arrows diff --git a/Examples/ConditionalFormatting/TextComparisons.ps1 b/Examples/ConditionalFormatting/TextComparisons.ps1 index 78ccbb6..0a759bd 100644 --- a/Examples/ConditionalFormatting/TextComparisons.ps1 +++ b/Examples/ConditionalFormatting/TextComparisons.ps1 @@ -1,6 +1,4 @@ -cls - -ipmo ..\..\ImportExcel.psd1 -Force +try {ipmo ..\..\ImportExcel.psd1 -Force} catch {} $data = $( New-PSItem 100 (echo test testx) @@ -10,8 +8,8 @@ $data = $( New-PSItem 500 ) -$file1 = "tryComparison1.xlsx" -$file2 = "tryComparison2.xlsx" +$file1 = "$env:Temp\tryComparison1.xlsx" +$file2 = "$env:Temp\tryComparison2.xlsx" rm $file1 -ErrorAction Ignore rm $file2 -ErrorAction Ignore @@ -22,6 +20,6 @@ $data | Export-Excel $file1 -Show -ConditionalText $( ) $data | Export-Excel $file2 -Show -ConditionalText $( - New-ConditionalText -ConditionalType GreaterThanOrEqual 275 + New-ConditionalText -ConditionalType GreaterThanOrEqual 275 New-ConditionalText -ConditionalType LessThanOrEqual 250 -BackgroundColor cyan ) diff --git a/Examples/CustomReporting/dashboard.xlsx b/Examples/CustomReporting/dashboard.xlsx deleted file mode 100644 index c2afb01..0000000 Binary files a/Examples/CustomReporting/dashboard.xlsx and /dev/null differ diff --git a/Examples/Fibonacci/fib.xlsx b/Examples/Fibonacci/fib.xlsx deleted file mode 100644 index 431ef10..0000000 Binary files a/Examples/Fibonacci/fib.xlsx and /dev/null differ diff --git a/Examples/HyperLinks/First10Races.csv b/Examples/HyperLinks/First10Races.csv new file mode 100644 index 0000000..46b3180 --- /dev/null +++ b/Examples/HyperLinks/First10Races.csv @@ -0,0 +1,101 @@ +Race,Date,FinishPosition,Driver,GridPosition,Team,Points +Australian,25/03/2018,1,Sebastian Vettel,3,Ferrari,25 +Australian,25/03/2018,2,Lewis Hamilton,1,Mercedes,18 +Australian,25/03/2018,3,Kimi Räikkönen,2,Ferrari,15 +Australian,25/03/2018,4,Daniel Ricciardo,8,Red Bull Racing-TAG Heuer,12 +Australian,25/03/2018,5,Fernando Alonso,10,McLaren-Renault,10 +Australian,25/03/2018,6,Max Verstappen,4,Red Bull Racing-TAG Heuer,8 +Australian,25/03/2018,7,Nico Hülkenberg,7,Renault,6 +Australian,25/03/2018,8,Valtteri Bottas,15,Mercedes,4 +Australian,25/03/2018,9,Stoffel Vandoorne,11,McLaren-Renault,2 +Australian,25/03/2018,10,Carlos Sainz,9,Renault,1 +Bahrain,08/04/2018,1,Sebastian Vettel,1,Ferrari,25 +Bahrain,08/04/2018,2,Valtteri Bottas,3,Mercedes,18 +Bahrain,08/04/2018,3,Lewis Hamilton,9,Mercedes,15 +Bahrain,08/04/2018,4,Pierre Gasly,5,STR-Honda,12 +Bahrain,08/04/2018,5,Kevin Magnussen,6,Haas-Ferrari,10 +Bahrain,08/04/2018,6,Nico Hülkenberg,7,Renault,8 +Bahrain,08/04/2018,7,Fernando Alonso,13,McLaren-Renault,6 +Bahrain,08/04/2018,8,Stoffel Vandoorne,14,McLaren-Renault,4 +Bahrain,08/04/2018,9,Marcus Ericsson,17,Sauber-Ferrari,2 +Bahrain,08/04/2018,10,Esteban Ocon,8,Force India-Mercedes,1 +Chinese,15/04/2018,1,Daniel Ricciardo,6,Red Bull Racing-TAG Heuer,25 +Chinese,15/04/2018,2,Valtteri Bottas,3,Mercedes,18 +Chinese,15/04/2018,3,Kimi Räikkönen,2,Ferrari,15 +Chinese,15/04/2018,4,Lewis Hamilton,4,Mercedes,12 +Chinese,15/04/2018,5,Max Verstappen,5,Red Bull Racing-TAG Heuer,10 +Chinese,15/04/2018,6,Nico Hülkenberg,7,Renault,8 +Chinese,15/04/2018,7,Fernando Alonso,13,McLaren-Renault,6 +Chinese,15/04/2018,8,Sebastian Vettel,1,Ferrari,4 +Chinese,15/04/2018,9,Carlos Sainz,9,Renault,2 +Chinese,15/04/2018,10,Kevin Magnussen,11,Haas-Ferrari,1 +Azerbaijan,29/04/2018,1,Lewis Hamilton,2,Mercedes,25 +Azerbaijan,29/04/2018,2,Kimi Räikkönen,6,Ferrari,18 +Azerbaijan,29/04/2018,3,Sergio Pérez,8,Force India-Mercedes,15 +Azerbaijan,29/04/2018,4,Sebastian Vettel,1,Ferrari,12 +Azerbaijan,29/04/2018,5,Carlos Sainz,9,Renault,10 +Azerbaijan,29/04/2018,6,Charles Leclerc,13,Sauber-Ferrari,8 +Azerbaijan,29/04/2018,7,Fernando Alonso,12,McLaren-Renault,6 +Azerbaijan,29/04/2018,8,Lance Stroll,10,Williams-Mercedes,4 +Azerbaijan,29/04/2018,9,Stoffel Vandoorne,16,McLaren-Renault,2 +Azerbaijan,29/04/2018,10,Brendon Hartley,19,STR-Honda,1 +Spanish,13/05/2018,1,Lewis Hamilton,1,Mercedes,25 +Spanish,13/05/2018,2,Valtteri Bottas,2,Mercedes,18 +Spanish,13/05/2018,3,Max Verstappen,5,Red Bull Racing-TAG Heuer,15 +Spanish,13/05/2018,4,Sebastian Vettel,3,Ferrari,12 +Spanish,13/05/2018,5,Daniel Ricciardo,6,Red Bull Racing-TAG Heuer,10 +Spanish,13/05/2018,6,Kevin Magnussen,7,Haas-Ferrari,8 +Spanish,13/05/2018,7,Carlos Sainz,9,Renault,6 +Spanish,13/05/2018,8,Fernando Alonso,8,McLaren-Renault,4 +Spanish,13/05/2018,9,Sergio Pérez,15,Force India-Mercedes,2 +Spanish,13/05/2018,10,Charles Leclerc,14,Sauber-Ferrari,1 +Monaco,27/05/2018,1,Daniel Ricciardo,1,Red Bull Racing-TAG Heuer,25 +Monaco,27/05/2018,2,Sebastian Vettel,2,Ferrari,18 +Monaco,27/05/2018,3,Lewis Hamilton,3,Mercedes,15 +Monaco,27/05/2018,4,Kimi Räikkönen,4,Ferrari,12 +Monaco,27/05/2018,5,Valtteri Bottas,5,Mercedes,10 +Monaco,27/05/2018,6,Esteban Ocon,6,Force India-Mercedes,8 +Monaco,27/05/2018,7,Pierre Gasly,10,STR-Honda,6 +Monaco,27/05/2018,8,Nico Hülkenberg,11,Renault,4 +Monaco,27/05/2018,9,Max Verstappen,20,Red Bull Racing-TAG Heuer,2 +Monaco,27/05/2018,10,Carlos Sainz,8,Renault,1 +Canadian,10/06/2018,1,Sebastian Vettel,1,Ferrari,25 +Canadian,10/06/2018,2,Valtteri Bottas,2,Mercedes,18 +Canadian,10/06/2018,3,Max Verstappen,3,Red Bull Racing-TAG Heuer,15 +Canadian,10/06/2018,4,Daniel Ricciardo,6,Red Bull Racing-TAG Heuer,12 +Canadian,10/06/2018,5,Lewis Hamilton,4,Mercedes,10 +Canadian,10/06/2018,6,Kimi Räikkönen,5,Ferrari,8 +Canadian,10/06/2018,7,Nico Hülkenberg,7,Renault,6 +Canadian,10/06/2018,8,Carlos Sainz,9,Renault,4 +Canadian,10/06/2018,9,Esteban Ocon,8,Force India-Mercedes,2 +Canadian,10/06/2018,10,Charles Leclerc,13,Sauber-Ferrari,1 +French,24/06/2018,1,Lewis Hamilton,1,Mercedes,25 +French,24/06/2018,2,Max Verstappen,4,Red Bull Racing-TAG Heuer,18 +French,24/06/2018,3,Kimi Räikkönen,6,Ferrari,15 +French,24/06/2018,4,Daniel Ricciardo,5,Red Bull Racing-TAG Heuer,12 +French,24/06/2018,5,Sebastian Vettel,3,Ferrari,10 +French,24/06/2018,6,Kevin Magnussen,9,Haas-Ferrari,8 +French,24/06/2018,7,Valtteri Bottas,2,Mercedes,6 +French,24/06/2018,8,Carlos Sainz,7,Renault,4 +French,24/06/2018,9,Nico Hülkenberg,12,Renault,2 +French,24/06/2018,10,Charles Leclerc,8,Sauber-Ferrari,1 +Austrian,01/07/2018,1,Max Verstappen,4,Red Bull Racing-TAG Heuer,25 +Austrian,01/07/2018,2,Kimi Räikkönen,3,Ferrari,18 +Austrian,01/07/2018,3,Sebastian Vettel,6,Ferrari,15 +Austrian,01/07/2018,4,Romain Grosjean,5,Haas-Ferrari,12 +Austrian,01/07/2018,5,Kevin Magnussen,8,Haas-Ferrari,10 +Austrian,01/07/2018,6,Esteban Ocon,11,Force India-Mercedes,8 +Austrian,01/07/2018,7,Sergio Pérez,15,Force India-Mercedes,6 +Austrian,01/07/2018,8,Fernando Alonso,20,McLaren-Renault,4 +Austrian,01/07/2018,9,Charles Leclerc,17,Sauber-Ferrari,2 +Austrian,01/07/2018,10,Marcus Ericsson,18,Sauber-Ferrari,1 +British,08/07/2018,1,Sebastian Vettel,2,Ferrari,25 +British,08/07/2018,2,Lewis Hamilton,1,Mercedes,18 +British,08/07/2018,3,Kimi Räikkönen,3,Ferrari,15 +British,08/07/2018,4,Valtteri Bottas,4,Mercedes,12 +British,08/07/2018,5,Daniel Ricciardo,6,Red Bull Racing-TAG Heuer,10 +British,08/07/2018,6,Nico Hülkenberg,11,Renault,8 +British,08/07/2018,7,Esteban Ocon,10,Force India-Mercedes,6 +British,08/07/2018,8,Fernando Alonso,13,McLaren-Renault,4 +British,08/07/2018,9,Kevin Magnussen,7,Haas-Ferrari,2 +British,08/07/2018,10,Sergio Pérez,12,Force India-Mercedes,1 \ No newline at end of file diff --git a/Examples/HyperLinks/Races.ps1 b/Examples/HyperLinks/Races.ps1 new file mode 100644 index 0000000..fefe945 --- /dev/null +++ b/Examples/HyperLinks/Races.ps1 @@ -0,0 +1,27 @@ + +#First 10 races is a CSV file containing the top 10 finishers for the first 10 Formula one races of 2018. Read this file and group the results by race +#We will create links to each race in the first 10 rows of the spreadSheet +#The next row will be column labels +#After that will come a block for each race. + +#Read the data, and decide how much space to leave for the hyperlinks +$scriptPath = Split-Path -Path $MyInvocation.MyCommand.path -Parent +$dataPath = Join-Path -Path $scriptPath -ChildPath "First10Races.csv" +$results = Import-Csv -Path $dataPath | Group-Object -Property RACE +$topRow = $lastDataRow = 1 + $results.Count + +#Export the first row of the first group (race) with headers. +$path = "$env:TEMP\Results.xlsx" +Remove-Item -Path $path -ErrorAction SilentlyContinue +$excel = $results[0].Group[0] | Export-Excel -Path $path -StartRow $TopRow -BoldTopRow -PassThru + +#export each group (race) below the last one, without headers, and create a range for each using the group (Race) name +foreach ($r in $results) { + $excel = $R.Group | Export-Excel -ExcelPackage $excel -NoHeader -StartRow ($lastDataRow +1) -RangeName $R.Name -PassThru -AutoSize + $lastDataRow += $R.Group.Count +} + +#Create a hyperlink for each property with display text of "RaceNameGP" which links to the range created when the rows were exported a +$results | ForEach-Object {(New-Object -TypeName OfficeOpenXml.ExcelHyperLink -ArgumentList "Sheet1!$($_.Name)" , "$($_.name) GP")} | + Export-Excel -ExcelPackage $excel -AutoSize -Show + diff --git a/Examples/Index - Music.ps1 b/Examples/Index - Music.ps1 new file mode 100644 index 0000000..862ae07 --- /dev/null +++ b/Examples/Index - Music.ps1 @@ -0,0 +1,15 @@ +Remove-item -path ~\documents\music.xlsx +$excel = Get-IndexedItem "itemtype='.mp3'","AlbumArtist like '%'","RatingText <> '1 star'" -NoFiles -orderby AlbumArtist,AlbumTitle,TrackNumber -path c:\users -Recurse -Property AlbumArtist,Duration,title,EncodingBitrate,SampleRate,AlbumTitle,TrackNumber, Size | + Select-Object -Property AlbumArtist, AlbumTitle, TrackNumber, Title, Duration, SampleRate, EncodingBitRate, Size | Export-excel -path ~\documents\music.xlsx -WorksheetName Music -AutoNameRange -AutoSize -BoldTopRow -FreezeTopRow -PassThru +$ws = $excel.Workbook.Worksheets[1] +Set-ExcelColumn -Worksheet $ws -Column 6 -NumberFormat '0,"KHz"' +Set-ExcelColumn -Worksheet $ws -Column 7 -NumberFormat '0,"Kbits/Sec"' -Width 18 +Set-ExcelColumn -Worksheet $ws -Column 8 -NumberFormat '#.#,,"MB"' -Width 7 +$pt = Add-PivotTable -PivotTableName SpaceUsedByMusic -ExcelPackage $excel -SourceWorkSheet $ws -PivotRows ALBUMARTIST -PivotData @{"Size"="Sum"} -PivotNumberFormat '#.#,,"MB"' -Activate -PassThru +$pt.RowFields[0].Sort = [OfficeOpenXml.Table.PivotTable.eSortType]::Ascending + +$a = $ws.Dimension.address +Add-ExcelTable -Range $ws.cells[$a] -TableStyle Light1 -TableName Musictable -ShowFilter:$false -ShowTotal -ShowFirstColumn +Add-ConditionalFormatting -Address $ws.Names[1] -RuleType ContainsText -ConditionValue "Hits" -ForeGroundColor Blue +Add-ConditionalFormatting -Address $ws.Cells["Albumartist"] -RuleType ContainsText -ConditionValue "Numan" -ForeGroundColor red +Close-ExcelPackage -show $excel \ No newline at end of file diff --git a/Examples/JoinWorksheet/Join-Worksheet.sample.ps1 b/Examples/JoinWorksheet/Join-Worksheet.sample.ps1 index 94ba68f..da3d468 100644 --- a/Examples/JoinWorksheet/Join-Worksheet.sample.ps1 +++ b/Examples/JoinWorksheet/Join-Worksheet.sample.ps1 @@ -40,4 +40,4 @@ $ptdef = New-PivotTableDefinition -PivotTableName "Summary" -PivotRows "Store" - #Put in a title and freeze to top of the sheet including title and colmun headings #Add the Pivot table. #Show the result -Join-Worksheet -Path $path -WorkSheetName "Total" -Clearsheet -FromLabel "Store" -TableName "Summary" -TableStyle Light1 -AutoSize -BoldTopRow -FreezePane 2,1 -Title "Store Sales Summary" -TitleBold -TitleSize 14 -PivotTableDefinition $ptdef -show +Join-Worksheet -Path $path -WorkSheetName "Total" -Clearsheet -FromLabel "Store" -TableName "Combined" -TableStyle Light1 -AutoSize -BoldTopRow -FreezePane 2,1 -Title "Store Sales Summary" -TitleBold -TitleSize 14 -PivotTableDefinition $ptdef -show diff --git a/Examples/JoinWorksheet/Join-worksheet-blocks.sample.ps1 b/Examples/JoinWorksheet/Join-worksheet-blocks.sample.ps1 index 5ee0506..b19fcdb 100644 --- a/Examples/JoinWorksheet/Join-worksheet-blocks.sample.ps1 +++ b/Examples/JoinWorksheet/Join-worksheet-blocks.sample.ps1 @@ -1,11 +1,13 @@ $path = "$env:TEMP\Test.xlsx" Remove-item -Path $path -ErrorAction SilentlyContinue - Get-WmiObject -Class win32_logicaldisk | +#Export disk volume, and Network adapter to their own sheets. +Get-WmiObject -Class win32_logicaldisk | Select-Object -Property DeviceId,VolumeName, Size,Freespace | Export-Excel -Path $path -WorkSheetname Volumes -NumberFormat "0,000" Get-NetAdapter | Select-Object -Property Name,InterfaceDescription,MacAddress,LinkSpeed | Export-Excel -Path $path -WorkSheetname NetAdapters +#Create a summary page with a title of Summary, label the blocks with the name of the sheet they came from and hide the source sheets Join-Worksheet -Path $path -HideSource -WorkSheetName Summary -NoHeader -LabelBlocks -AutoSize -Title "Summary" -TitleBold -TitleSize 22 -show diff --git a/Examples/JoinWorksheet/JoinSalesData.ps1 b/Examples/JoinWorksheet/JoinSalesData.ps1 index a094ffd..f8938f1 100644 --- a/Examples/JoinWorksheet/JoinSalesData.ps1 +++ b/Examples/JoinWorksheet/JoinSalesData.ps1 @@ -11,12 +11,13 @@ $params = @{ ExcelChartDefinition = New-ExcelChartDefinition -XRange Item -YRange UnitSold -Title 'Units Sold' Path = $xlfile } - +#Import 4 sets of sales data from 4 CSV files, using the parameters above. Import-Csv $PSScriptRoot\NorthSales.csv | Export-Excel -WorkSheetname North @params Import-Csv $PSScriptRoot\EastSales.csv | Export-Excel -WorkSheetname East @params Import-Csv $PSScriptRoot\SouthSales.csv | Export-Excel -WorkSheetname South @params Import-Csv $PSScriptRoot\WestSales.csv | Export-Excel -WorkSheetname West @params +#Join the 4 worksheets together on a sheet named Allsales, use the same parameters, except for AutoNameRange and ExcelChartDefinition. $params.Remove("AutoNameRange") $params.Remove("ExcelChartDefinition") Join-Worksheet -WorkSheetName AllSales -Show @params \ No newline at end of file diff --git a/Examples/MergeWorkSheet/Merge_2_Servers_Services.ps1 b/Examples/MergeWorkSheet/Merge_2_Servers_Services.ps1 new file mode 100644 index 0000000..d6c7b29 --- /dev/null +++ b/Examples/MergeWorkSheet/Merge_2_Servers_Services.ps1 @@ -0,0 +1,21 @@ +Remove-Item -Path "$env:temp\server*.xlsx" , "$env:temp\Combined*.xlsx" -ErrorAction SilentlyContinue + +#Get a subset of services into $s and export them +[System.Collections.ArrayList]$s = get-service | Select-Object -first 25 -Property * +$s | Export-Excel -Path $env:temp\server1.xlsx + +#$s is a zero based array, excel rows are 1 based and excel has a header row so Excel rows will be 2 + index in $s. +#Change a row. Add a row. Delete a row. And export the changed $s to a second file. +$s[2].DisplayName = "Changed from the orginal" #This will be row 4 in Excel - this should be highlighted as a change + +$d = $s[-1] | Select-Object -Property * +$d.DisplayName = "Dummy Service" +$d.Name = "Dummy" +$s.Insert(3,$d) #This will be row 5 in Excel - this should be highlighted as a new item + +$s.RemoveAt(5) #This will be row 7 in Excel - this should be highlighted as deleted item + +$s | Export-Excel -Path $env:temp\server2.xlsx + +#This use of Merge-worksheet Assumes a default worksheet name, (sheet1) We will check and output Name (the key), DisplayName and StartType and ignore other properties. +Merge-Worksheet -Referencefile "$env:temp\server1.xlsx" -Differencefile "$env:temp\Server2.xlsx" -OutputFile "$env:temp\combined1.xlsx" -Property name,displayname,startType -Key name -Show \ No newline at end of file diff --git a/Examples/MergeWorkSheet/Merge_3_Servers_Services.ps1 b/Examples/MergeWorkSheet/Merge_3_Servers_Services.ps1 new file mode 100644 index 0000000..fec6917 --- /dev/null +++ b/Examples/MergeWorkSheet/Merge_3_Servers_Services.ps1 @@ -0,0 +1,34 @@ +Remove-Item -Path "$env:temp\server*.xlsx" , "$env:temp\Combined*.xlsx" -ErrorAction SilentlyContinue + +#Get a subset of services into $s and export them +[System.Collections.ArrayList]$s = get-service | Select-Object -first 25 -Property Name,DisplayName,StartType +$s | Export-Excel -Path $env:temp\server1.xlsx + +#$s is a zero based array, excel rows are 1 based and excel has a header row so Excel rows will be 2 + index in $s. +#Change a row. Add a row. Delete a row. And export the changed $s to a second file. +$row4Displayname = $s[2].DisplayName +$s[2].DisplayName = "Changed from the orginal" #This will be excel row 4 and Server 2 will show as changed. + +$d = $s[-1] | Select-Object -Property * +$d.DisplayName = "Dummy Service" +$d.Name = "Dummy" +$s.Insert(3,$d) #This will be Excel row 5 and server 2 will show as changed - so will Server 3 + +$s.RemoveAt(5) #This will be Excel row 7 and Server 2 will show as missing. + +$s | Export-Excel -Path $env:temp\server2.xlsx + +#Make some more changes to $s and export it to a third file +$s[2].displayname = $row4Displayname #Server 3 row 4 will match server 1 so won't be highlighted + +$d = $s[-1] | Select-Object -Property * +$d.DisplayName = "Second Service" +$d.Name = "Service2" +$s.Insert(6,$d) #This will be an extra row in Server 3 at row 8. It will show as missing in Server 2. +$s.RemoveAt(8) #This will show as missing in Server 3 at row 11 () + +$s | Export-Excel -Path $env:temp\server3.xlsx + +#Now bring the three files together. + +Merge-MultipleSheets -Path "$env:temp\server1.xlsx", "$env:temp\Server2.xlsx","$env:temp\Server3.xlsx" -OutputFile "$env:temp\combined3.xlsx" -Property name,displayname,startType -Key name -Show \ No newline at end of file diff --git a/Examples/MoveSheets/MoveSheets.ps1 b/Examples/MoveSheets/MoveSheets.ps1 index b5723f4..6b6887b 100644 --- a/Examples/MoveSheets/MoveSheets.ps1 +++ b/Examples/MoveSheets/MoveSheets.ps1 @@ -3,7 +3,7 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} $xlfile = "$env:TEMP\testThis.xlsx" Remove-Item $xlfile -ErrorAction Ignore -1..10 | Export-Excel $xlfile -WorkSheetname First -11..20 | Export-Excel $xlfile -WorkSheetname Second -MoveToStart -21..30 | Export-Excel $xlfile -WorkSheetname Third -MoveBefore First -31..40 | Export-Excel $xlfile -WorkSheetname Fourth -MoveAfter Third -Show \ No newline at end of file +1..10 | Export-Excel $xlfile -WorkSheetname First #'First' will be the only sheet +11..20 | Export-Excel $xlfile -WorkSheetname Second -MoveToStart #'Second' is moved before first so the order is 'Second', 'First' +21..30 | Export-Excel $xlfile -WorkSheetname Third -MoveBefore First #'Second' is moved before first so the order is 'Second', 'Third', 'First' +31..40 | Export-Excel $xlfile -WorkSheetname Fourth -MoveAfter Third -Show #'Fourth' is moved after third so the order is ' 'Second', 'Third', 'Fourth' First' \ No newline at end of file diff --git a/Examples/NumberFormat/ColorizeNumbers.ps1 b/Examples/NumberFormat/ColorizeNumbers.ps1 index 18a2dc1..1352432 100644 --- a/Examples/NumberFormat/ColorizeNumbers.ps1 +++ b/Examples/NumberFormat/ColorizeNumbers.ps1 @@ -1,6 +1,6 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -$file = "disks.xlsx" +$file = "$env:TEMP\disks.xlsx" Remove-Item $file -ErrorAction Ignore @@ -11,5 +11,5 @@ $data = $( New-PSItem -3.2 -4.1 New-PSItem -5.2 6.1 ) - +#Set the numbers throughout the sheet to format as positive in blue with a + sign, negative in Red with a - sign. $data | Export-Excel -Path $file -Show -AutoSize -NumberFormat "[Blue]+0.#0;[Red]-0.#0" diff --git a/Examples/NumberFormat/CurrencyFormat.ps1 b/Examples/NumberFormat/CurrencyFormat.ps1 index e7c327a..d6f589d 100644 --- a/Examples/NumberFormat/CurrencyFormat.ps1 +++ b/Examples/NumberFormat/CurrencyFormat.ps1 @@ -1,6 +1,6 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -$file = "disks.xlsx" +$file = "$env:temp\disks.xlsx" Remove-Item $file -ErrorAction Ignore @@ -12,6 +12,5 @@ $data = $( New-PSItem -5.2 6.1 New-PSItem 1000 -2000 ) - -$data | Export-Excel -Path $file -Show -AutoSize -NumberFormat '[Blue]$#,##0.00;[Red]-$#,##0.00' - +#Number format can expand terms like Currency, to the local currency format +$data | Export-Excel -Path $file -Show -AutoSize -NumberFormat 'Currency' diff --git a/Examples/NumberFormat/disks.xlsx b/Examples/NumberFormat/disks.xlsx deleted file mode 100644 index 79baae1..0000000 Binary files a/Examples/NumberFormat/disks.xlsx and /dev/null differ diff --git a/Examples/PassThru/TryPassThru.ps1 b/Examples/PassThru/TryPassThru.ps1 index c33060f..a80e176 100644 --- a/Examples/PassThru/TryPassThru.ps1 +++ b/Examples/PassThru/TryPassThru.ps1 @@ -1,16 +1,20 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -$file = "sales.xlsx" +$file = "$env:Temp\sales.xlsx" Remove-Item $file -ErrorAction Ignore +#Using -Passthru with Export-Excel returns an Excel Package object. $xlPkg = Import-Csv .\sales.csv | Export-Excel $file -PassThru -$ws = $xlPkg.Workbook.WorkSheets[1] +#We add script properties to the package so $xlPkg.Sheet1 is equivalent to $xlPkg.Workbook.WorkSheets["Sheet1"] +$ws = $xlPkg.Sheet1 +#We can manipulate the cells ... $ws.Cells["E1"].Value = "TotalSold" $ws.Cells["F1"].Value = "Add 10%" +#This is for illustration - there are more efficient ways to do this. 2..($ws.Dimension.Rows) | ForEach-Object { $ws.Cells["E$_"].Formula = "=C$_+D$_" @@ -19,7 +23,7 @@ $ws.Cells["F1"].Value = "Add 10%" $ws.Cells.AutoFitColumns() +#You can call close-ExcelPackage $xlPkg -show, but here we will do the ssteps explicitly $xlPkg.Save() $xlPkg.Dispose() - Invoke-Item $file \ No newline at end of file diff --git a/Examples/PassThru/sales.xlsx b/Examples/PassThru/sales.xlsx deleted file mode 100644 index ba69c5b..0000000 Binary files a/Examples/PassThru/sales.xlsx and /dev/null differ diff --git a/Examples/PivotTable/PivotTableWithName.ps1 b/Examples/PivotTable/PivotTableWithName.ps1 index 45524ea..ae0c395 100644 --- a/Examples/PivotTable/PivotTableWithName.ps1 +++ b/Examples/PivotTable/PivotTableWithName.ps1 @@ -1,15 +1,25 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -Remove-Item .\test1.xlsx -ErrorAction Ignore - $ExcelParams = @{ - Path = ".\test1.xlsx" + Path = "$env:TEMP\test1.xlsx" IncludePivotTable = $true PivotRows = 'Company' PivotTableName = 'MyTable' PivotData = @{'Handles' = 'sum'} Show = $true + Activate = $true } +Remove-Item $ExcelParams.Path -ErrorAction Ignore +Get-Process | Select-Object Company, Handles | Export-Excel @ExcelParams -Get-Process | Select-Object Company, Handles | - Export-Excel @ExcelParams \ No newline at end of file +<# Builds a pivot table that looks like this: + + Sum of Handles + Row Labels Total + Adobe Systems Incorporated 3100 + (blank) 214374 + Apple Inc. 215 + etc + etc + Grand Total 365625 +#> \ No newline at end of file diff --git a/Examples/PivotTable/TableAndPivotTable.ps1 b/Examples/PivotTable/TableAndPivotTable.ps1 new file mode 100644 index 0000000..97f6e0f --- /dev/null +++ b/Examples/PivotTable/TableAndPivotTable.ps1 @@ -0,0 +1,26 @@ +$path = "$Env:TEMP\test.xlsx" +remove-item -path $path -ErrorAction SilentlyContinue + +#Export some sales data to Excel, format it as a table and put a data-bar in. For this example we won't create the pivot table during the export +$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 -PassThru -Path $path -TableStyle Medium13 -tablename "RawData" -ConditionalFormat @{Range="C2:C7"; DataBarColor="Green"} + +#Add a pivot table, specify its address to put it on the same sheet, use the data that was just exported set the table style and number format. +#Use the "City" for the row names, and "Product" for the columnnames, and sum both the gross and net values for each City/Product combination; add grand totals to rows and columns. +# activate the sheet and add a pivot chart (defined in a hash table) +Add-PivotTable -Address $excel.Sheet1.Cells["F1"] -SourceWorkSheet $Excel.Sheet1 -SourceRange $Excel.Sheet1.Dimension.Address -PivotTableName "Sales" -PivotTableSyle "Medium12" -PivotNumberFormat "$#,##0.00" ` + -PivotRows "City" -PivotColumns "Product" -PivotData @{Gross="Sum";Net="Sum"}-PivotTotals "Both" -Activate -PivotChartDefinition @{ + Title="Gross and net by city and product"; + ChartType="ColumnClustered"; + Column=11; Width=500; Height=360; + YMajorUnit=500; YMinorUnit=100; YAxisNumberformat="$#,##0" + LegendPostion="Bottom"} +#Save and open in excel +Close-ExcelPackage $excel -Show \ No newline at end of file diff --git a/Examples/PivotTable/test1.xlsx b/Examples/PivotTable/test1.xlsx deleted file mode 100644 index 182c72e..0000000 Binary files a/Examples/PivotTable/test1.xlsx and /dev/null differ diff --git a/Examples/PivotTableFilters/dashboard.xlsx b/Examples/PivotTableFilters/dashboard.xlsx deleted file mode 100644 index f6fa0fb..0000000 Binary files a/Examples/PivotTableFilters/dashboard.xlsx and /dev/null differ diff --git a/Examples/PivotTableFilters/testPivot.xlsx b/Examples/PivotTableFilters/testPivot.xlsx deleted file mode 100644 index 443beb0..0000000 Binary files a/Examples/PivotTableFilters/testPivot.xlsx and /dev/null differ diff --git a/Examples/PivotTableFilters/testPivotFilter.ps1 b/Examples/PivotTableFilters/testPivotFilter.ps1 index b112690..33b89fd 100644 --- a/Examples/PivotTableFilters/testPivotFilter.ps1 +++ b/Examples/PivotTableFilters/testPivotFilter.ps1 @@ -1,6 +1,6 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -$xlFile=".\testPivot.xlsx" +$xlFile="$env:TEMP\testPivot.xlsx" Remove-Item $xlFile -ErrorAction Ignore $data =@" @@ -18,4 +18,18 @@ $data | -AutoSize -AutoFilter ` -IncludePivotTable ` -PivotRows Product ` - -PivotData @{"Units"="sum"} -PivotFilter Region, Area + -PivotData @{"Units"="sum"} -PivotFilter Region, Area -Activate + +<# +Creates a Pivot table that looks like +Region All^ +Area All^ + +Sum of Units +Row Labels Total +Apple 100 +Pear 240 +Grape 280 +Banana 160 +Grand Total 780 +#> \ No newline at end of file diff --git a/Examples/SetColumnBackgroundColor/SetColumnBackgroundColor.ps1 b/Examples/SetColumnBackgroundColor/SetColumnBackgroundColor.ps1 index 47c416e..36580c1 100644 --- a/Examples/SetColumnBackgroundColor/SetColumnBackgroundColor.ps1 +++ b/Examples/SetColumnBackgroundColor/SetColumnBackgroundColor.ps1 @@ -1,10 +1,13 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -$p = Get-Process | Select-Object Company, Handles | Export-Excel c:\temp\testBackgroundColor.xlsx -ClearSheet -KillExcel -PassThru +$path = "$env:TEMP\testBackgroundColor.xlsx" + +$p = Get-Process | Select-Object Company, Handles | Export-Excel $path -ClearSheet -PassThru $ws = $p.Workbook.WorkSheets[1] $totalRows = $ws.Dimension.Rows -Set-Format -Address $ws.Cells["B2:B$($totalRows)"] -BackgroundColor LightBlue +#Set the range from B2 to the last active row. s +Set-ExcelRange -Range $ws.Cells["B2:B$($totalRows)"] -BackgroundColor LightBlue Export-Excel -ExcelPackage $p -show -AutoSize \ No newline at end of file diff --git a/Examples/SpreadsheetCells/CalculatedFields.ps1 b/Examples/SpreadsheetCells/CalculatedFields.ps1 index 622f99d..2a4c139 100644 --- a/Examples/SpreadsheetCells/CalculatedFields.ps1 +++ b/Examples/SpreadsheetCells/CalculatedFields.ps1 @@ -3,7 +3,7 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} #. ..\New-PSItem.ps1 -Remove-Item *.xlsx +Remove-Item "$env:temp\functions.xlsx" -ErrorAction SilentlyContinue $( New-PSItem 12001 Nails 37 3.99 =C2*D2 (echo ID Product Quantity Price Total) @@ -11,4 +11,4 @@ $( New-PSItem 12003 Saw 12 15.37 =C4*D4 New-PSItem 12010 Drill 20 8 =C5*D5 New-PSItem 12011 Crowbar 7 23.48 =C6*D6 -) | Export-Excel functions.xlsx -AutoSize -Show +) | Export-Excel "$env:temp\functions.xlsx"-AutoSize -Show diff --git a/Examples/SpreadsheetCells/ExcelFunctions.ps1 b/Examples/SpreadsheetCells/ExcelFunctions.ps1 index 0e341c5..a5aa6f4 100644 --- a/Examples/SpreadsheetCells/ExcelFunctions.ps1 +++ b/Examples/SpreadsheetCells/ExcelFunctions.ps1 @@ -1,6 +1,6 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -Remove-Item *.xlsx +Remove-Item "$env:temp\functions.xlsx" -ErrorAction SilentlyContinue $( New-PSItem =2%/12 60 500000 "=pmt(rate,nper,pv)" (echo rate nper pv pmt) @@ -9,4 +9,4 @@ $( New-PSItem =5%/12 60 500000 "=pmt(rate,nper,pv)" New-PSItem =6%/12 60 500000 "=pmt(rate,nper,pv)" New-PSItem =7%/12 60 500000 "=pmt(rate,nper,pv)" -) | Export-Excel functions.xlsx -AutoNameRange -AutoSize -Show \ No newline at end of file +) | Export-Excel "$env:temp\functions.xlsx" -AutoNameRange -AutoSize -Show \ No newline at end of file diff --git a/Examples/SpreadsheetCells/HyperLink.ps1 b/Examples/SpreadsheetCells/HyperLink.ps1 index 451534e..36e92fe 100644 --- a/Examples/SpreadsheetCells/HyperLink.ps1 +++ b/Examples/SpreadsheetCells/HyperLink.ps1 @@ -1,10 +1,10 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -Remove-Item *.xlsx +Remove-Item "$env:temp\hyperlink.xlsx" -ErrorAction SilentlyContinue $( New-PSItem '=Hyperlink("http://dougfinke.com/blog","Doug Finke")' @("Link") New-PSItem '=Hyperlink("http://blogs.msdn.com/b/powershell/","PowerShell Blog")' New-PSItem '=Hyperlink("http://blogs.technet.com/b/heyscriptingguy/","Hey, Scripting Guy")' -) | Export-Excel hyperlink.xlsx -AutoSize -Show +) | Export-Excel "$env:temp\hyperlink.xlsx" -AutoSize -Show diff --git a/Examples/SpreadsheetCells/hyperlink.xlsx b/Examples/SpreadsheetCells/hyperlink.xlsx deleted file mode 100644 index a8b5e06..0000000 Binary files a/Examples/SpreadsheetCells/hyperlink.xlsx and /dev/null differ diff --git a/Examples/Stocks/stocks.xlsx b/Examples/Stocks/stocks.xlsx deleted file mode 100644 index 6eb02d9..0000000 Binary files a/Examples/Stocks/stocks.xlsx and /dev/null differ diff --git a/Examples/Tables/MultipleTables.ps1 b/Examples/Tables/MultipleTables.ps1 index 86d8752..03f8a89 100644 --- a/Examples/Tables/MultipleTables.ps1 +++ b/Examples/Tables/MultipleTables.ps1 @@ -1,7 +1,7 @@ try {. $PSScriptRoot\..\..\LoadPSD1.ps1} catch {} -$xlfile = "testData.xlsx" -Remove-Item *.xlsx +$xlfile = "$env:Temp\testData.xlsx" +Remove-Item $xlfile -ErrorAction SilentlyContinue $r = Get-ChildItem C:\WINDOWS\system32 @@ -23,16 +23,19 @@ $top10ByFileSize = $r | Select-Object -First 10 Name, @{n="Size";e={$_.Length}} #,Extension,Path -$top10BySize | Export-Excel $xlfile -WorkSheetname FileInfo -TableName ExtSize -$top10ByCount | Export-Excel $xlfile -WorkSheetname FileInfo -StartRow 13 -TableName ExtCount -$top10ByFileSize | Export-Excel $xlfile -WorkSheetname FileInfo -StartRow 25 -AutoSize -TableName FileSize +$xlPkg = $top10BySize | Export-Excel -path $xlfile -WorkSheetname FileInfo -TableName ExtSize -PassThru +$xlPkg = $top10ByCount | Export-Excel -ExcelPackage $xlPkg -WorkSheetname FileInfo -StartRow 13 -TableName ExtCount -PassThru +$xlPkg = $top10ByFileSize | Export-Excel -ExcelPackage $xlPkg -WorkSheetname FileInfo -StartRow 25 -TableName FileSize -PassThru -AutoSize + +#worksheets.tables["Name1","Name2"] returns 2 tables. Set-ExcelRange can process those and will set the number format over both +Set-ExcelRange -Range $xlpkg.Workbook.Worksheets[1].Tables["ExtSize","FileSize"] -NumberFormat '0,,"MB"' $ps = Get-Process | Where-Object Company $ps | Sort-Object handles -Descending | Select-Object -First 10 company, handles | - Export-Excel $xlfile -WorkSheetname Handles -AutoSize -TableName Handles + Export-Excel -ExcelPackage $xlPkg -WorkSheetname Handles -AutoSize -TableName Handles $ps | Sort-Object PM -Descending | diff --git a/Examples/Tables/testData.xlsx b/Examples/Tables/testData.xlsx deleted file mode 100644 index d94934a..0000000 Binary files a/Examples/Tables/testData.xlsx and /dev/null differ diff --git a/Export-Excel.ps1 b/Export-Excel.ps1 index 5f3db16..10b26c1 100644 --- a/Export-Excel.ps1 +++ b/Export-Excel.ps1 @@ -47,7 +47,9 @@ .PARAMETER PivotFilter 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 . + 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 @@ -171,7 +178,8 @@ Export all the processes to the Excel file 'Test.xlsx' and open the file immediately. .EXAMPLE - $ExcelParams = @{ + > + PS> $ExcelParams = @{ Path = $env:TEMP + '\Excel.xlsx' Show = $true Verbose = $true @@ -183,7 +191,8 @@ Exports all data to the Excel file 'Excel.xslx' and colors the negative values in 'Red' and the positive values in 'Blue'. It will also add a dollar sign '$' in front of the rounded numbers to two decimal characters behind the comma. .EXAMPLE - $ExcelParams = @{ + > + PS> $ExcelParams = @{ Path = $env:TEMP + '\Excel.xlsx' Show = $true Verbose = $true @@ -208,7 +217,8 @@ Exports all data to the Excel file 'Excel.xslx' and tries to convert all values to numbers where possible except for 'IPAddress' and 'Number1'. These are stored in the sheet 'as is', without being converted to a number. .EXAMPLE - $ExcelParams = @{ + > + PS> $ExcelParams = @{ Path = $env:TEMP + '\Excel.xlsx' Show = $true Verbose = $true @@ -233,7 +243,8 @@ Exports all data to the Excel file 'Excel.xslx' as is, no number conversion will take place. This means that Excel will show the exact same data that you handed over to the 'Export-Excel' function. .EXAMPLE - $ExcelParams = @{ + > + PS> $ExcelParams = @{ Path = $env:TEMP + '\Excel.xlsx' Show = $true Verbose = $true @@ -247,7 +258,8 @@ Exports data that will have a 'Conditional formatting rule' in Excel on these cells that will show the background fill color in 'LightPink' and the text color in 'DarkRed' when the value is greater then '525'. In case this condition is not met the color will be the default, black text on a white background. .EXAMPLE - $ExcelParams = @{ + > + PS> $ExcelParams = @{ Path = $env:TEMP + '\Excel.xlsx' Show = $true Verbose = $true @@ -262,7 +274,8 @@ Export all services to an Excel sheet where all cells have a 'Conditional formatting rule' in Excel that will show the background fill color in 'LightPink' and the text color in 'DarkRed' when the value contains the word 'Stop'. If the value contains the word 'Running' it will have a background fill color in 'Cyan' and a text color 'Blue'. In case none of these conditions are met the color will be the default, black text on a white background. .EXAMPLE - $ExcelParams = @{ + > + PS> $ExcelParams = @{ Path = $env:TEMP + '\Excel.xlsx' Show = $true Verbose = $true @@ -305,7 +318,8 @@ Get-Service | Export-Excel 'c:\temp\test.xlsx' -Show -IncludePivotTable -PivotRows status -PivotData @{status='count'} .EXAMPLE - $pt = [ordered]@{} + > + PS> $pt = [ordered]@{} $pt.pt1=@{ SourceWorkSheet = 'Sheet1'; PivotRows = 'Status' PivotData = @{'Status'='count'} @@ -328,7 +342,8 @@ .EXAMPLE - Remove-Item -Path .\test.xlsx + > + PS> Remove-Item -Path .\test.xlsx $excel = Get-Service | Select-Object -Property Status,Name,DisplayName,StartType | Export-Excel -Path .\test.xlsx -PassThru $excel.Workbook.Worksheets["Sheet1"].Row(1).style.font.bold = $true $excel.Workbook.Worksheets["Sheet1"].Column(3 ).width = 29 @@ -341,29 +356,33 @@ It then uses the package object to apply formatting, and then saves the workbook and disposes of the object before loading the document in Excel. .EXAMPLE - Remove-Item -Path .\test.xlsx -ErrorAction Ignore + > + PS> Remove-Item -Path .\test.xlsx -ErrorAction Ignore $excel = Get-Process | Select-Object -Property Name,Company,Handles,CPU,PM,NPM,WS | Export-Excel -Path .\test.xlsx -ClearSheet -WorksheetName "Processes" -PassThru $sheet = $excel.Workbook.Worksheets["Processes"] - $sheet.Column(1) | Set-Format -Bold -AutoFit - $sheet.Column(2) | Set-Format -Width 29 -WrapText - $sheet.Column(3) | Set-Format -HorizontalAlignment Right -NFormat "#,###" - Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NFormat "#,###" - Set-Format -Address $sheet.Column(4) -HorizontalAlignment Right -NFormat "#,##0.0" -Bold - Set-Format -Address $sheet.Row(1) -Bold -HorizontalAlignment Center + $sheet.Column(1) | Set-ExcelRange -Bold -AutoFit + $sheet.Column(2) | Set-ExcelRange -Width 29 -WrapText + $sheet.Column(3) | Set-ExcelRange -HorizontalAlignment Right -NFormat "#,###" + Set-ExcelRange -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NFormat "#,###" + Set-ExcelRange -Address $sheet.Column(4) -HorizontalAlignment Right -NFormat "#,##0.0" -Bold + Set-ExcelRange -Address $sheet.Row(1) -Bold -HorizontalAlignment Center Add-ConditionalFormatting -WorkSheet $sheet -Range "D2:D1048576" -DataBarColor Red Add-ConditionalFormatting -WorkSheet $sheet -Range "G2:G1048576" -RuleType GreaterThan -ConditionValue "104857600" -ForeGroundColor Red - foreach ($c in 5..9) {Set-Format -Address $sheet.Column($c) -AutoFit } + foreach ($c in 5..9) {Set-ExcelRange -Address $sheet.Column($c) -AutoFit } Export-Excel -ExcelPackage $excel -WorksheetName "Processes" -IncludePivotChart -ChartType ColumnClustered -NoLegend -PivotRows company -PivotData @{'Name'='Count'} -Show - This a more sophisticated version of the previous example showing different ways of using Set-Format, and also adding conditional formatting. + This a more sophisticated version of the previous example showing different ways of using Set-ExcelRange, 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 #> - [CmdletBinding(DefaultParameterSetName = 'Default')] + [OutputType([OfficeOpenXml.ExcelPackage])] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] Param( [Parameter(ParameterSetName = "Default", Position = 0)] @@ -515,7 +534,8 @@ } { [System.Uri]::IsWellFormedUriString($_ , [System.UriKind]::Absolute) } { # Save a hyperlink : internal links can be in the form xl://sheet!E419 (use A1 as goto sheet), or xl://RangeName - if ($_ -match "^xl://internal/") { + if ($_ -is [uri]) {$targetCell.HyperLink = $_ } + elseif ($_ -match "^xl://internal/") { $referenceAddress = $_ -replace "^xl://internal/" , "" $display = $referenceAddress -replace "!A1$" , "" $h = New-Object -TypeName OfficeOpenXml.ExcelHyperLink -ArgumentList $referenceAddress , $display @@ -552,7 +572,7 @@ } } - Try { + try { $script:Header = $null if ($Append -and $ClearSheet) {throw "You can't use -Append AND -ClearSheet."} @@ -571,9 +591,9 @@ } Else { $pkg = Open-ExcelPackage -Path $Path -Create -KillExcel:$KillExcel -Password:$Password} } - Catch {throw "Could not open Excel Package $path"} + catch {throw "Could not open Excel Package $path"} if ($NoClobber) {Write-Warning -Message "-NoClobber parameter is no longer used" } - Try { + try { $params = @{WorksheetName=$WorksheetName} foreach ($p in @("ClearSheet", "MoveToStart", "MoveToEnd", "MoveBefore", "MoveAfter", "Activate")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}} $ws = $pkg | Add-WorkSheet @params @@ -582,17 +602,7 @@ $WorksheetName = $ws.Name } } - Catch {throw "Could not get worksheet $worksheetname"} - try { - foreach ($format in $ConditionalFormat ) { - switch ($format.formatter) { - "ThreeIconSet" {Add-ConditionalFormatting -WorkSheet $ws -ThreeIconsSet $format.IconType -range $format.range -reverse:$format.reverse } - "FourIconSet" {Add-ConditionalFormatting -WorkSheet $ws -FourIconsSet $format.IconType -range $format.range -reverse:$format.reverse } - "FiveIconSet" {Add-ConditionalFormatting -WorkSheet $ws -FiveIconsSet $format.IconType -range $format.range -reverse:$format.reverse } - } - } - } - catch {throw "Error applying confitional formatting to worksheet"} + catch {throw "Could not get worksheet $worksheetname"} try { if ($Append -and $ws.Dimension) { #if there is a title or anything else above the header row, append needs to be combined wih a suitable startrow parameter @@ -620,11 +630,11 @@ $AutoFilter = $false } #if we did not get $autofilter but a filter range is set and it covers the right area, set autofilter to true - elseif (-not $AutoFilter -and $ws.Names["_xlnm._FilterDatabase"]) { - if ( ($ws.Names["_xlnm._FilterDatabase"].Start.Row -eq $StartRow) -and - ($ws.Names["_xlnm._FilterDatabase"].Start.Column -eq $StartColumn) -and - ($ws.Names["_xlnm._FilterDatabase"].End.Row -eq $ws.Dimension.End.Row) -and - ($ws.Names["_xlnm._FilterDatabase"].End.Column -eq $ws.Dimension.End.Column) ) {$AutoFilter = $true} + elseif (-not $AutoFilter -and $ws.Names['_xlnm._FilterDatabase']) { + if ( ($ws.Names['_xlnm._FilterDatabase'].Start.Row -eq $StartRow) -and + ($ws.Names['_xlnm._FilterDatabase'].Start.Column -eq $StartColumn) -and + ($ws.Names['_xlnm._FilterDatabase'].End.Row -eq $ws.Dimension.End.Row) -and + ($ws.Names['_xlnm._FilterDatabase'].End.Column -eq $ws.Dimension.End.Column) ) {$AutoFilter = $true} } $row = $ws.Dimension.End.Row @@ -650,7 +660,7 @@ else { $Row = $StartRow } $ColumnIndex = $StartColumn $Numberformat = Expand-NumberFormat -NumberFormat $Numberformat - if ($row -le 2 -and $ColumnIndex -eq 1 -and $Numberformat -ne $ws.Cells.Style.Numberformat.Format) { + if ((-not $ws.Dimension) -and ($Numberformat -ne $ws.Cells.Style.Numberformat.Format)) { $ws.Cells.Style.Numberformat.Format = $Numberformat $setNumformat = $false } @@ -659,7 +669,7 @@ $firstTimeThru = $true $isDataTypeValueType = $false } - Catch { + catch { if ($AlreadyExists) { #Is this set anywhere ? throw "Failed exporting worksheet '$WorksheetName' to '$Path': The worksheet '$WorksheetName' already exists." @@ -672,10 +682,10 @@ Process { if ($TargetData) { - Try { + try { if ($firstTimeThru) { $firstTimeThru = $false - $isDataTypeValueType = $TargetData.GetType().name -match 'string|timespan|datetime|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort' + $isDataTypeValueType = $TargetData.GetType().name -match 'string|timespan|datetime|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort|URI|ExcelHyperLink' if ($isDataTypeValueType -and -not $Append) {$row -= 1} #row incremented before adding values, so it is set to the number of rows inserted at the end Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'" } @@ -724,7 +734,7 @@ #endregion } } - Catch { + catch { throw "Failed exporting data to worksheet '$WorksheetName' to '$Path': $_" } } @@ -746,7 +756,7 @@ Write-Debug "Data Range '$dataRange'" if ($AutoNameRange) { - Try { + try { if (-not $script:header) { # if there aren't any headers, use the the first row of data to name the ranges: this is the last point that headers will be used. $headerRange = $ws.Dimension.Address -replace "\d+$", $StartRow @@ -766,56 +776,28 @@ # but we have to add the start column on when referencing positions foreach ($c in 0..($LastCol - $StartColumn)) { $targetRangeName = $script:Header[$c] -replace '\W' , '_' - $targetColumn = $c + $StartColumn - $theRange = $ws.Cells[$targetRow, $targetColumn, $LastRow , $targetColumn ] - if ($ws.names[$targetRangeName]) { $ws.names[$targetRangeName].Address = $theRange.FullAddressAbsolute } - else {$ws.Names.Add($targetRangeName, $theRange) | Out-Null } - + Add-ExcelName -RangeName $targetRangeName -Range $ws.Cells[$targetRow, ($StartColumn + $c ), $LastRow, ($StartColumn + $c )] 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." } } } - Catch {Write-Warning -Message "Failed adding named ranges to worksheet '$WorksheetName': $_" } + catch {Write-Warning -Message "Failed adding named ranges to worksheet '$WorksheetName': $_" } } - if ($RangeName) { - try { - if ($RangeName -match "\W") { - Write-Warning -Message "At least one character in $RangeName is illegal in a range name and will be replaced with '_' . " - $RangeName = $RangeName -replace '\W', '_' - } - #If named range exists, update it, else create it - if ($ws.Names[$RangeName]) { $ws.Names[$rangename].Address = $ws.Cells[$dataRange].FullAddressAbsolute } - else {$ws.Names.Add($RangeName, $ws.Cells[$dataRange]) | Out-Null } - } - Catch { Write-Warning -Message "Failed adding range '$RangeName' to worksheet '$WorksheetName': $_" } - } + if ($RangeName) { Add-ExcelName -Range $ws.Cells[$dataRange] -RangeName $RangeName} if ($TableName) { - try { - if ($TableName -match "\W") { - Write-Warning -Message "At least one character in $TableName is illegal in a table name and will be replaced with '_' . " - $TableName = $TableName -replace '\W', '_' - } - #if the table exists, update it. - if ($ws.Tables[$TableName]) { - $ws.Tables[$TableName].TableXml.table.ref = $dataRange - $ws.Tables[$TableName].TableStyle = $TableStyle - } - else { - $tbl = $ws.Tables.Add($ws.Cells[$dataRange], $TableName) - $tbl.TableStyle = $TableStyle - } - Write-Verbose -Message "Defined table '$TableName' at $($targetRange.Address)" + if ($PSBoundParameters.ContainsKey('TableStyle')) { + Add-ExcelTable -Range $ws.Cells[$dataRange] -TableName $TableName -TableStyle $TableStyle } - catch {Write-Warning -Message "Failed adding table '$TableName' to worksheet '$WorksheetName': $_"} + else {Add-ExcelTable -Range $ws.Cells[$dataRange] -TableName $TableName} } if ($AutoFilter) { try { $ws.Cells[$dataRange].AutoFilter = $true - Write-Verbose -Message "Enabeld autofilter. " + Write-Verbose -Message "Enabled autofilter. " } catch {Write-Warning -Message "Failed adding autofilter to worksheet '$WorksheetName': $_"} } @@ -824,20 +806,25 @@ foreach ($item in $PivotTableDefinition.GetEnumerator()) { $params = $item.value if ($Activate) {$params.Activate = $true } - if ($params.keys -notcontains "SourceRange" -and - ($params.Keys -notcontains "SourceWorkSheet" -or $params.SourceWorkSheet -eq $WorksheetName)) {$params.SourceRange = $dataRange} - if ($params.Keys -notcontains "SourceWorkSheet") {$params.SourceWorkSheet = $ws } - if ($params.Keys -notcontains "NoTotalsInPivot" -and $NoTotalsInPivot ) {$params.PivotTotals = "None"} - if ($params.Keys -notcontains "PivotTotals" -and $PivotTotals ) {$params.PivotTotals = $PivotTotals} - if ($params.Keys -notcontains "PivotDataToColumn" -and $PivotDataToColumn) {$params.PivotDataToColumn = $true} + if ($params.keys -notcontains 'SourceRange' -and + ($params.Keys -notcontains 'SourceWorkSheet' -or $params.SourceWorkSheet -eq $WorksheetName)) {$params.SourceRange = $dataRange} + if ($params.Keys -notcontains 'SourceWorkSheet') {$params.SourceWorkSheet = $ws } + if ($params.Keys -notcontains 'NoTotalsInPivot' -and $NoTotalsInPivot ) {$params.PivotTotals = 'None'} + if ($params.Keys -notcontains 'PivotTotals' -and $PivotTotals ) {$params.PivotTotals = $PivotTotals} + if ($params.Keys -notcontains 'PivotDataToColumn' -and $PivotDataToColumn) {$params.PivotDataToColumn = $true} Add-PivotTable -ExcelPackage $pkg -PivotTableName $item.key @Params } } if ($IncludePivotTable -or $IncludePivotChart) { $params = @{ - "SourceRange" = $dataRange + 'SourceRange' = $dataRange } + if ($PivotTableName -and ($pkg.workbook.worksheets.tables.name -contains $PivotTableName)) { + Write-Warning -Message "The selected Pivot table name '$PivotTableName' is already used as a table name. Adding a suffix of 'Pivot'." + $PivotTableName += 'Pivot' + } + if ($PivotTableName) {$params.PivotTableName = $PivotTableName} else {$params.PivotTableName = $WorksheetName + 'PivotTable'} if ($Activate) {$params.Activate = $true } @@ -932,9 +919,19 @@ } foreach ($chartDef in $ExcelChartDefinition) { - $params = @{} - $chartDef.PSObject.Properties | ForEach-Object {if ( $null -ne $_.value) {$params[$_.name] = $_.value}} - Add-ExcelChart -Worksheet $ws @params + if ($chartDef -is [System.Management.Automation.PSCustomObject]) { + $params = @{} + $chartDef.PSObject.Properties | ForEach-Object {if ( $null -ne $_.value) {$params[$_.name] = $_.value}} + Add-ExcelChart -Worksheet $ws @params + } + elseif ($chartDef -is [hashtable] -or $chartDef -is[System.Collections.Specialized.OrderedDictionary]) { + Add-ExcelChart -Worksheet $ws @chartDef + } + } + + 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) { @@ -942,37 +939,61 @@ 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 - $params = @{ - XRange = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $xcol , $lastrow, $xcol) - YRange = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $ycol , $lastrow, $ycol) - Title = ""; - Column = ($lastCol +1) ; - Width = 800 + if (-not $xcol) { + $xcol = $StartColumn + $range = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, ($startColumn +1), $FirstDataRow, $lastCol ) } - if ($ShowPercent) {$params["ShowPercent"] = $true} - if ($ShowCategory) {$params["ShowCategory"] = $true} - if ($NoLegend) {$params["NoLegend"] = $true} - if (-not $NoHeader) {$params["SeriesHeader"] = $ws.Cells[$startRow, $YCol].Value} - if ($ColumnChart) {$Params["chartType"] = "ColumnStacked" } - elseif ($Barchart) {$Params["chartType"] = "BarStacked" } - elseif ($PieChart) {$Params["chartType"] = "PieExploded3D" } - elseif ($LineChart) {$Params["chartType"] = "Line" } + $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 = @{ + XRange = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $xcol , $lastrow, $xcol) + YRange = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $ycol , $lastrow, $ycol) + Title = '' + Column = ($lastCol +1) + Width = 800 + } + if ($ShowPercent) {$params["ShowPercent"] = $true} + if ($ShowCategory) {$params["ShowCategory"] = $true} + if ($NoLegend) {$params["NoLegend"] = $true} + if (-not $NoHeader) {$params["SeriesHeader"] = $ws.Cells[$startRow, $YCol].Value} + if ($ColumnChart) {$Params["chartType"] = "ColumnStacked" } + elseif ($Barchart) {$Params["chartType"] = "BarStacked" } + elseif ($PieChart) {$Params["chartType"] = "PieExploded3D" } + elseif ($LineChart) {$Params["chartType"] = "Line" } - Add-ExcelChart -Worksheet $ws @params + Add-ExcelChart -Worksheet $ws @params + } } - foreach ($ct in $ConditionalText) { + # It now doesn't matter if the conditional formating rules are passed in $conditionalText or $conditional format. + # Just one with an alias for compatiblity it will break things for people who are using both at once + foreach ($c in (@() + $ConditionalText + $ConditionalFormat) ) { try { - $cfParams = @{RuleType = $ct.ConditionalType; ConditionValue = $ct.text ; - BackgroundColor = $ct.BackgroundColor; BackgroundPattern = $ct.PatternType ; - ForeGroundColor = $ct.ConditionalTextColor + #we can take an object with a .ConditionalType property made by New-ConditionalText or with a .Formatter Property made by New-ConditionalFormattingIconSet or a hash table + if ($c.ConditionalType) { + $cfParams = @{RuleType = $c.ConditionalType; ConditionValue = $c.Text ; + BackgroundColor = $c.BackgroundColor; BackgroundPattern = $c.PatternType ; + ForeGroundColor = $c.ConditionalTextColor} + if ($c.Range) {$cfParams.Range = $c.Range} + else {$cfParams.Range = $ws.Dimension.Address } + Add-ConditionalFormatting -WorkSheet $ws @cfParams + Write-Verbose -Message "Added conditional formatting to range $($c.range)" + } + elseif ($c.formatter) { + switch ($c.formatter) { + "ThreeIconSet" {Add-ConditionalFormatting -WorkSheet $ws -ThreeIconsSet $c.IconType -range $c.range -reverse:$c.reverse } + "FourIconSet" {Add-ConditionalFormatting -WorkSheet $ws -FourIconsSet $c.IconType -range $c.range -reverse:$c.reverse } + "FiveIconSet" {Add-ConditionalFormatting -WorkSheet $ws -FiveIconsSet $c.IconType -range $c.range -reverse:$c.reverse } + } + Write-Verbose -Message "Added conditional formatting to range $($c.range)" + } + elseif ($c -is [hashtable] -or $c -is[System.Collections.Specialized.OrderedDictionary]) { + if (-not $c.Range) {$c.Range = $ws.Dimension.Address } + Add-ConditionalFormatting -WorkSheet $ws @c } - if ($ct.Range) {$cfParams.range = $ct.range} else { $cfParams.Range = $ws.Dimension.Address } - Add-ConditionalFormatting -WorkSheet $ws @cfParams - Write-Verbose -Message "Added conditional formatting to range $($ct.range)" } - catch {Write-Warning -Message "Failed adding conditional formatting to worksheet '$WorksheetName': $_"} + catch {throw "Error applying conditional formatting to worksheet $_"} } if ($CellStyleSB) { @@ -984,15 +1005,10 @@ 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 { $ws.Protection.SetPassword($Password) - Write-Verbose -Message "Set password on workbook" + Write-Verbose -Message 'Set password on workbook' } catch {throw "Failed setting password for worksheet '$WorksheetName': $_"} @@ -1008,7 +1024,7 @@ if ($ReZip) { Write-Verbose -Message "Re-Zipping $($pkg.file) using .NET ZIP library" try { - Add-Type -AssemblyName "System.IO.Compression.Filesystem" -ErrorAction stop + Add-Type -AssemblyName 'System.IO.Compression.Filesystem' -ErrorAction stop } catch { Write-Error "The -ReZip parameter requires .NET Framework 4.5 or later to be installed. Recommend to install Powershell v4+" @@ -1031,96 +1047,29 @@ } } -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 - - 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, - #If specified a chart Will be included. - [Switch]$IncludePivotChart, - #Optional title for the pivot chart, by default the title omitted. - [String]$ChartTitle, - #Height of the chart in Pixels (400 by default) - [int]$ChartHeight = 400 , - #Width of the chart in Pixels (600 by default) - [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). - [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) - [Int]$ChartColumn = 4, - #Vertical offset of the chart from the cell corner. - [Int]$ChartRowOffSetPixels = 0 , - #Horizontal offset of the chart from the cell corner. - [Int]$ChartColumnOffSetPixels = 0, - #Type of chart - [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie', - #If specified hides the chart legend - [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. - [Switch]$ShowCategory, - #If specified attaches percentages to slices in a pie chart. - [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') - - @{$PivotTableName = $parameters} -} function Add-WorkSheet { <# .Synopsis Adds a workshet to an existing workbook. .Description - If the named worksheet already exists, the -clearsheet parameter decides whether it should be deleted and a new one returned, - or if not specified the existing sheet will be returned. + If the named worksheet already exists, the -Clearsheet parameter decides whether it should be deleted and a new one returned, + or if not specified the existing sheet will be returned. By default the sheet is created at the end of the work book, the + -MoveXXXX switches allow the sheet to be [re]positioned at the start or before or after another sheet. A new sheet will only be + made the default sheet when excel opens if -Activate is specified. + .Example + $WorksheetActors = $ExcelPackage | Add-WorkSheet -WorkSheetname Actors + + $ExcelPackage holds an Excel package object (returned by Open-ExcelPackage, or Export-Excel -passthru). + This command will add a sheet named 'Actors', or return the sheet if it exists, and the result is stored in $WorkSheetActors. + .Example + $WorksheetActors = Add-WorkSheet -ExcelPackage $ExcelPackage -WorkSheetname "Actors" -ClearSheet -MoveToStart + + This time the Excel package object is passed as a parameter instead of piped. If the 'Actors' sheet already exists it is deleted + and re-created. The new sheet will be created last in the workbook, and -MoveToStart Moves it to the start. + .Example + $null = Add-WorkSheet -ExcelWorkbook $wb -WorkSheetname $DestinationName -CopySource $sourceWs -Activate + This time a workbook is used instead of a package, and a worksheet is copied - $SourceWs is a worksheet object, which can come + from the same workbook or a different one. Here the new copy of the data is made the active sheet when the workbook is opened. #> [cmdletBinding()] [OutputType([OfficeOpenXml.ExcelWorksheet])] @@ -1158,7 +1107,7 @@ function Add-WorkSheet { if ($ExcelPackage -and -not $ExcelWorkbook) {$ExcelWorkbook = $ExcelPackage.Workbook} # If WorksheetName was given, try to use that worksheet. If it wasn't, and we are copying an existing sheet, try to use the sheet name - # if we are not copying a sheet or the name is in use, use the name "SheetX" where X is the number of the new sheet + # If we are not copying a sheet, and have no name, use the name "SheetX" where X is the number of the new sheet if (-not $WorksheetName -and $CopySource -and -not $ExcelWorkbook[$CopySource.Name]) {$WorksheetName = $CopySource.Name} elseif (-not $WorksheetName) {$WorksheetName = "Sheet" + (1 + $ExcelWorkbook.Worksheets.Count)} else {$ws = $ExcelWorkbook.Worksheets[$WorksheetName]} @@ -1166,7 +1115,7 @@ function Add-WorkSheet { #If -clearsheet was specified and the named sheet exists, delete it if ($ws -and $ClearSheet) { $ExcelWorkbook.Worksheets.Delete($WorksheetName) ; $ws = $null } - #copy or create new sheet as needed + #Copy or create new sheet as needed if (-not $ws -and $CopySource) { Write-Verbose -Message "Copying into worksheet '$WorksheetName'." $ws = $ExcelWorkbook.Worksheets.Add($WorksheetName, $CopySource) @@ -1208,281 +1157,190 @@ function Add-WorkSheet { else {Write-Warning "Can't find worksheet '$MoveAfter'; worsheet '$WorksheetName' will not be moved."} } #endregion - if ($Activate) {Select-Worksheet -ExcelWorkbook $ExcelWorkbook -WorksheetName $ws.Name } + if ($Activate) {Select-Worksheet -ExcelWorksheet $ws } + if ($ExcelPackage -and -not (Get-Member -InputObject $ExcelPackage -Name $ws.Name)) { + $sb = [scriptblock]::Create(('$this.workbook.Worksheets["{0}"]' -f $ws.name)) + Add-Member -InputObject $ExcelPackage -MemberType ScriptProperty -Name $ws.name -Value $sb + } return $ws } -function Add-PivotTable { -<# - .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. -#> - param ( - #Name for the new Pivot table - this will be the name of a sheet in the workbook - [Parameter(Mandatory = $true)] - $PivotTableName, - #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, - #If specified a chart Will be included. - [Switch]$IncludePivotChart, - #Optional title for the pivot chart, by default the title omitted. - [String]$ChartTitle, - #Height of the chart in Pixels (400 by default) - [int]$ChartHeight = 400 , - #Width of the chart in Pixels (600 by default) - [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). - [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) - [Int]$ChartColumn = 4, - #Vertical offset of the chart from the cell corner. - [Int]$ChartRowOffSetPixels = 0 , - #Horizontal offset of the chart from the cell corner. - [Int]$ChartColumnOffSetPixels = 0, - #Type of chart - [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie', - #If specified hides the chart legend - [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. - [Switch]$ShowCategory, - #If specified attaches percentages to slices in a pie chart. - [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 - ) - $pivotTableDataName = $pivotTableName + 'PivotTableData' - [OfficeOpenXml.ExcelWorksheet]$wsPivot = Add-WorkSheet -ExcelPackage $ExcelPackage -WorksheetName $pivotTableName -Activate:$Activate - - #if the pivot doesn't exist, create it. - if (-not $wsPivot.PivotTables[$pivotTableDataName] ) { - 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 ($PivotFilter) {$PivotTableStartCell = "A3"} else { $PivotTableStartCell = "A1"} - if (-not $SourceRange) { $SourceRange = $SourceWorkSheet.Dimension.Address} - $pivotTable = $wsPivot.PivotTables.Add($wsPivot.Cells[$PivotTableStartCell], $SourceWorkSheet.Cells[ $SourceRange], $pivotTableDataName) - } - 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.$_ - } - 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 } - } - 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[$pivotTableDataName] - $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['PivotChart'] ) { - try { - [OfficeOpenXml.Drawing.Chart.ExcelChart] $chart = $wsPivot.Drawings.AddChart('PivotChart', $ChartType, $pivotTable) - $chart.SetPosition($ChartRow , $ChartRowOffSetPixels , $ChartColumn, $ChartColumnOffSetPixels) - $chart.SetSize( $ChartWidth, $ChartHeight) - if ($chart.DataLabel) { - $chart.DataLabel.ShowCategory = [boolean]$ShowCategory - $chart.DataLabel.ShowPercent = [boolean]$ShowPercent - } - if ($NoLegend) { $chart.Legend.Remove()} - if ($ChartTitle) {$chart.Title.Text = $ChartTitle} - } - catch { catch {Write-Warning -Message "Failed adding chart for pivotable '$pivotTableName': $_"} - } - - } -} -function Add-ExcelChart { - <# - .Synopsis - Creates a chart in an Existing excel worksheet - #> - [cmdletbinding()] - param( - #An object representing the worksheet where the chart should be added. - [OfficeOpenXml.ExcelWorksheet]$Worksheet, - [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, - [int]$Width = 500, - [int]$Height = 350, - [int]$Row = 0, - [int]$RowOffSetPixels = 10, - [int]$Column = 6, - [int]$ColumnOffSetPixels = 5, - [OfficeOpenXml.Drawing.Chart.eLegendPosition]$LegendPostion, - $LegendSize, - [Switch]$legendBold, - [Switch]$NoLegend, - [Switch]$ShowCategory, - [Switch]$ShowPercent, - $SeriesHeader, - [Switch]$TitleBold, - [Int]$TitleSize , - [String]$XAxisTitleText, - [Switch]$XAxisTitleBold, - $XAxisTitleSize , - [string]$XAxisNumberformat, - $XMajorUnit, - $XMinorUnit, - $XMaxValue, - $XMinValue, - [OfficeOpenXml.Drawing.Chart.eAxisPosition]$XAxisPosition , - [String]$YAxisTitleText, - [Switch]$YAxisTitleBold, - $YAxisTitleSize, - [string]$YAxisNumberformat, - $YMajorUnit, - $YMinorUnit, - $YMaxValue, - $YMinValue, - [OfficeOpenXml.Drawing.Chart.eAxisPosition]$YAxisPosition ) - try { - $ChartName = 'Chart' + (Split-Path -Leaf ([System.IO.path]::GetTempFileName())) -replace 'tmp|\.', '' - $chart = $Worksheet.Drawings.AddChart($ChartName, $ChartType) - if ($Title) { - $chart.Title.Text = $Title - if ($TitleBold) {$chart.Title.Font.Bold = $true} - if ($TitleSize) {$chart.Title.Font.Size = $TitleSize} - } - if ($NoLegend) { $chart.Legend.Remove() } - else { - if ($LegendPostion) {$Chart.Legend.Position = $LegendPostion} - if ($LegendSize) {$chart.Legend.Font.Size = $LegendSize} - if ($legendBold) {$chart.Legend.Font.Bold = $true} - } - - if ($XAxisTitleText) { - $chart.XAxis.Title.Text = $XAxisTitleText - if ($XAxisTitleBold) {$chart.XAxis.Title.Font.Bold = $true} - if ($XAxisTitleSize) {$chart.XAxis.Title.Font.Size = $XAxisTitleSize} - } - if ($XAxisPosition) {Write-Warning "X Axis position is not being set propertly at the moment, parameter ignored" } - #$chart.ChartXml.chartSpace.chart.plotArea.catAx.axPos.val = $XAxisPosition.ToString().substring(0,1)} - if ($XMajorUnit) {$chart.XAxis.MajorUnit = $XMajorUnit} - if ($XMinorUnit) {$chart.XAxis.MinorUnit = $XMinorUnit} - if ($null -ne $XMinValue) {$chart.XAxis.MinValue = $XMinValue} - if ($null -ne $XMaxValue) {$chart.XAxis.MaxValue = $XMaxValue} - if ($XAxisNumberformat) {$chart.XAxis.Format = (Expand-NumberFormat $XAxisNumberformat)} - - if ($YAxisTitleText) { - $chart.YAxis.Title.Text = $YAxisTitleText - if ($YAxisTitleBold) {$chart.YAxis.Title.Font.Bold = $true} - if ($YAxisTitleSize) {$chart.YAxis.Title.Font.Size = $YAxisTitleSize} - } - if ($YAxisPosition) {Write-Warning "Y Axis position is not being set propertly at the moment, parameter ignored" } - #$chart.ChartXml.chartSpace.chart.plotArea.valAx.axPos.val= $YAxisPosition.ToString().substring(0,1)} - if ($YMajorUnit) {$chart.YAxis.MajorUnit = $YMajorUnit} - if ($YMinorUnit) {$chart.YAxis.MinorUnit = $YMinorUnit} - if ($null -ne $YMinValue){$chart.YAxis.MinValue = $YMinValue} - if ($null -ne $YMaxValue){$chart.YAxis.MaxValue = $YMaxValue} - if ($YAxisNumberformat) {$chart.YAxis.Format = (Expand-NumberFormat $YAxisNumberformat)} - if ($null -ne $chart.Datalabel) { - $chart.Datalabel.ShowCategory = [boolean]$ShowCategory - $chart.Datalabel.ShowPercent = [boolean]$ShowPercent - } - - $chart.SetPosition($Row, $RowOffsetPixels, $Column, $ColumnOffsetPixels) - $chart.SetSize($Width, $Height) - - $chartDefCount = @($YRange).Count - if ($chartDefCount -eq 1) { - $Series = $chart.Series.Add($YRange, $XRange) - if ($SeriesHeader) { $Series.Header = $SeriesHeader} - else { $Series.Header = 'Series 1'} - } - else { - for ($idx = 0; $idx -lt $chartDefCount; $idx += 1) { - $Series = $chart.Series.Add($YRange[$idx], $XRange) - if ($SeriesHeader.Count -gt 0) { $Series.Header = $SeriesHeader[$idx] } - else { $Series.Header = "Series $($idx)"} - } - } - } - catch {Write-Warning -Message "Failed adding Chart to worksheet '$($WorkSheet).name': $_"} -} function Select-Worksheet { + <# + .SYNOPSIS + Sets the selected tab in an Excel workbook to be the chosen sheet, and unselects all the others. + .DESCRIPTION + Sometimes when a sheet is added we want it to be the active sheet, sometimes we want the active sheet to be left as it was. + Select-Worksheet exists to change the which sheet is the selected tab when Excel opens the file. + .EXAMPLE + Select-Worksheet -ExcelWorkbook $ExcelWorkbook -WorksheetName "NewSheet" + $ExcelWorkbook holds the a workbook object containing a sheet named "NewSheet"; + This sheet will become the [only] active sheet in the workbook + .EXAMPLE + Select-Worksheet -ExcelPackage $Pkg -WorksheetName "NewSheet2" + $pkg holds an Excel Package, whose workbook contains a sheet named "NewSheet2" + This sheet will become the [only] active sheet in the workbook + .EXAMPLE + Select-Worksheet -ExcelWorksheet $ws + $ws holds an Excel worksheet which will become the [only] active sheet in the workbook + #> param ( #An object representing an Excel Package. - [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Package", Position = 0)] + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'Package', Position = 0)] [OfficeOpenXml.ExcelPackage]$ExcelPackage, - #An Excel workbook to which the Worksheet will be added - a package contains one workbook so you can use whichever fits at the time. - [Parameter(Mandatory = $true, ParameterSetName = "WorkBook")] + #An Excel workbook to which the Worksheet will be added - a package contains one workbook so you can use workbook or package as it suits + [Parameter(Mandatory = $true, ParameterSetName = 'WorkBook')] [OfficeOpenXml.ExcelWorkbook]$ExcelWorkbook, + [Parameter(ParameterSetName='Package')] + [Parameter(ParameterSetName='Workbook')] #The name of the worksheet 'Sheet1' by default. - [string]$WorksheetName + [string]$WorksheetName, + #An object representing an Excel worksheet + [Parameter(ParameterSetName='Sheet',Mandatory=$true)] + [OfficeOpenXml.ExcelWorksheet]$ExcelWorksheet ) - #if we were given a workbook use it, if we were given a package, use its workbook - if ($ExcelPackage -and -not $ExcelWorkbook) {$ExcelWorkbook = $ExcelPackage.Workbook} - if (-not $ExcelWorkbook.Worksheets.Where({$_.name -eq $WorksheetName})) { - Write-Warning -Message "Workbook does not contain a worksheet named '$WorksheetName'" ; return - } + #if we were given a package, use its workbook + if ($ExcelPackage -and -not $ExcelWorkbook) {$ExcelWorkbook = $ExcelPackage.Workbook} + #if we now have workbook, get the worksheet; if we were given a sheet get the workbook + if ($ExcelWorkbook -and $WorksheetName) {$ExcelWorksheet = $ExcelWorkbook.Worksheets[$WorksheetName]} + elseif ($ExcelWorksheet -and -not $ExcelWorkbook) {$ExcelWorkbook = $ExcelWorksheet.Workbook ; } + #if we didn't get to a worksheet give up. If we did set all works sheets to not selected and then the one we want to selected. + if (-not $ExcelWorksheet) {Write-Warning -Message "The worksheet $WorksheetName was not found." ; return } else { foreach ($w in $ExcelWorkbook.Worksheets) {$w.View.TabSelected = $false} - $ExcelWorkbook.Worksheets[$WorksheetName].View.TabSelected = $true + $ExcelWorksheet.View.TabSelected = $true } +} + +Function Add-ExcelName { + <# + .SYNOPSIS + 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 is a string describing 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)] + [OfficeOpenXml.ExcelRange]$Range, + #The name to assign to the range. If the name exists it will be updated to the new range. If no name is specified the first cell in the range will be used as the name + [String]$RangeName + ) + try { + $ws = $Range.Worksheet + if (-not $RangeName) { + $RangeName = $ws.Cells[$Range.Start.Address].Value + $Range = ($Range.Worksheet.cells[($range.start.row +1), $range.start.Column , $range.end.row, $range.end.column]) + } + if ($RangeName -match '\W') { + Write-Warning -Message "Range name '$RangeName' contains illegal characters, they will be replaced with '_'." + $RangeName = $RangeName -replace '\W','_' + } + if ($ws.names[$RangeName]) { + Write-verbose -Message "Updating Named range '$RangeName' to $($Range.FullAddressAbsolute)." + $ws.Names[$RangeName].Address = $Range.FullAddressAbsolute + } + else { + Write-verbose -Message "Creating Named range '$RangeName' as $($Range.FullAddressAbsolute)." + $ws.Names.Add($RangeName, $Range) | Out-Null + } + } + catch {Write-Warning -Message "Failed adding named range '$RangeName' to worksheet '$($ws.Name)': $_" } +} + +function Add-ExcelTable { + <# + .SYNOPSIS + Adds Tables to Excel workbooks. + .DESCRIPTION + Unlike named ranges, where the name only needs to be unique within a sheet, Table names must be unique in the workbook + Tables carry formatting by default have a filter. The filter, header, Totals, first and last column highlights + .EXAMPLE + Add-ExcelTable -Range $ws.Cells[$dataRange] -TableName $TableName + + $WS is a worksheet, and $dataRange is a string describing a range of cells - e.g. "A1:Z10" + this range which will become a table, named $TableName + .EXAMPLE + Add-ExcelTable -Range $ws.cells[$($ws.Dimension.address)] -TableStyle Light1 -TableName Musictable -ShowFilter:$false -ShowTotal -ShowFirstColumn + 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)] + [OfficeOpenXml.ExcelRange]$Range, + #The name for the table + [Parameter(Mandatory=$true)] + [String]$TableName, + #The Style for the table, by default Medium 6 + [OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6', + #By default the header row is shown - it can be turned off with -ShowHeader:$false + [Switch]$ShowHeader , + #By default the filter is enabled - it can be turned off with -ShowFilter:$false + [Switch]$ShowFilter, + #Show total adds a totals row. This does not automatically sum the columns but provides a drop-down in each to select sum, average etc + [Switch]$ShowTotal, + #Hashtable in the form ColumnName = "Average"|"Count"|"CountNums"|"Max"|"Min"|"None"|"StdDev"|"Sum"|"Var" - if specified ShowTotal is not needed. + [hashtable]$TotalSettings, + #Highlights the first column in bold + [Switch]$ShowFirstColumn, + #Highlights the last column in bold + [Switch]$ShowLastColumn, + #By default the table formats show striped rows, the can be turned off with -ShowRowStripes:$false + [Switch]$ShowRowStripes, + #Turns on column stripes. + [Switch]$ShowColumnStripes, + #If -PassThru is specified the table object will be returned to allow additional + [Switch]$PassThru + ) + try { + if ($TableName -match "\W") { + Write-Warning -Message "At least one character in $TableName is illegal in a table name and will be replaced with '_' . " + $TableName = $TableName -replace '\W', '_' + } + $ws = $Range.Worksheet + #if the table exists in this worksheet, update it. + if ($ws.Tables[$TableName]) { + $tbl =$ws.Tables[$TableName] + $tbl.TableXml.table.ref = $Range.Address + Write-Verbose -Message "Re-defined table '$TableName', now at $($Range.Address)." + } + elseif ($ws.Workbook.Worksheets.Tables.Name -contains $TableName) { + Write-Warning -Message "The Table name '$TableName' is already used on a different worksheet." + return + } + else { + $tbl = $ws.Tables.Add($Range, $TableName) + Write-Verbose -Message "Defined table '$TableName' at $($Range.Address)" + } + #it seems that show total changes some of the others, so the sequence matters. + if ($PSBoundParameters.ContainsKey('ShowHeader')) {$tbl.ShowHeader = [bool]$ShowHeader} + if ($PSBoundParameters.ContainsKey('TotalSettings')) { + $tbl.ShowTotal = $true + foreach ($k in $TotalSettings.keys) { + if (-not $tbl.Columns[$k]) {Write-Warning -Message "Table does not have a Column '$k'."} + elseif ($TotalSettings[$k] -notin @("Average", "Count", "CountNums", "Max", "Min", "None", "StdDev", "Sum", "Var") ) { + Write-wanring "'$($TotalSettings[$k])' is not a valid total function." + } + else {$tbl.Columns[$k].TotalsRowFunction = $TotalSettings[$k]} + } + } + elseif ($PSBoundParameters.ContainsKey('ShowTotal')) {$tbl.ShowTotal = [bool]$ShowTotal} + if ($PSBoundParameters.ContainsKey('ShowFilter')) {$tbl.ShowFilter = [bool]$ShowFilter} + if ($PSBoundParameters.ContainsKey('ShowFirstColumn')) {$tbl.ShowFirstColumn = [bool]$ShowFirstColumn} + if ($PSBoundParameters.ContainsKey('ShowLastColumn')) {$tbl.ShowLastColumn = [bool]$ShowLastColumn} + if ($PSBoundParameters.ContainsKey('ShowRowStripes')) {$tbl.ShowRowStripes = [bool]$ShowRowStripes} + if ($PSBoundParameters.ContainsKey('ShowColumnStripes')) {$tbl.ShowColumnStripes = [bool]$ShowColumnStripes} + if ($PSBoundParameters.ContainsKey('TableStyle')) {$tbl.TableStyle = $TableStyle} + + if ($PassThru) {return $tbl} + } + catch {Write-Warning -Message "Failed adding table '$TableName' to worksheet '$WorksheetName': $_"} } \ No newline at end of file diff --git a/GetExcelTable.ps1 b/GetExcelTable.ps1 index e1aacc0..1bd7903 100644 --- a/GetExcelTable.ps1 +++ b/GetExcelTable.ps1 @@ -25,7 +25,7 @@ Function Get-ExcelTableName { $Stream.Close() $Stream.Dispose() $Excel.Dispose() - $Excel = $null + $Excel = $null } Function Get-ExcelTable { @@ -66,7 +66,7 @@ Function Get-ExcelTable { $propertyNames = for($col=$startCol; $col -lt ($startCol+$colCount); $col+= 1) { $Worksheet.Cells[$startRow, $col].value } - + $startRow++ for($row=$startRow; $row -lt ($startRow+$rowCount); $row += 1) { $nr=[ordered]@{} @@ -92,13 +92,11 @@ function ConvertFrom-ExcelColumnName { ForEach { $sum*=26 $sum+=[char]$_.tostring().toupper()-[char]'A'+1 - } + } $sum } -cls - ipmo .\ImportExcel.psd1 -Force -#Get-ExcelTableName .\testTable.xlsx | Get-ExcelTable .\testTable.xlsx +#Get-ExcelTableName .\testTable.xlsx | Get-ExcelTable .\testTable.xlsx Get-ExcelTable .\testTable.xlsx Table3 \ No newline at end of file diff --git a/ImportExcel.psm1 b/ImportExcel.psm1 index ca38aa0..107b8ec 100644 --- a/ImportExcel.psm1 +++ b/ImportExcel.psm1 @@ -28,6 +28,7 @@ . $PSScriptRoot\New-PSItem.ps1 . $PSScriptRoot\Open-ExcelPackage.ps1 . $PSScriptRoot\Pivot.ps1 + . $PSScriptRoot\PivotTable.ps1 . $PSScriptRoot\Send-SQLDataToExcel.ps1 . $PSScriptRoot\Set-CellStyle.ps1 . $PSScriptRoot\Set-Column.ps1 diff --git a/Join-Worksheet.ps1 b/Join-Worksheet.ps1 index 8786d1e..58264a2 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. @@ -9,31 +8,36 @@ In the former case the header row is copied from the first sheet and, by default, each row of data is labelled with the name of the sheet it came from. In the latter case -NoHeader is specified, and each copied block can have the sheet it came from placed above it as a title. .EXAMPLE - foreach ($computerName in @('Server1', 'Server2', 'Server3', 'Server4')) { - Get-Service -ComputerName $computerName | Select-Object -Property Status, Name, DisplayName, StartType | - Export-Excel -Path .\test.xlsx -WorkSheetname $computerName -AutoSize - } - $ptDef =New-PivotTableDefinition -PivotTableName "Pivot1" -SourceWorkSheet "Combined" -PivotRows "Status" -PivotFilter "MachineName" -PivotData @{Status='Count'} -IncludePivotChart -ChartType BarClustered3D - Join-Worksheet -Path .\test.xlsx -WorkSheetName combined -FromLabel "MachineName" -HideSource -AutoSize -FreezeTopRow -BoldTopRow -PivotTableDefinition $pt -Show - - The foreach command gets the services running on four servers and exports each to its own page in Test.xlsx. - $PtDef= creates a defintion for a single Pivot table. - The Join-Worksheet command uses the same file and merges the results onto a sheet named "Combined". It sets a column header of "Machinename", - this column will contain the name of the sheet the data was copied from; after copying the data to the sheet "combined", the other sheets will be hidden. - Join-Worksheet finishes by calling export-Excel to AutoSize cells, freeze the top row and make it bold and add the Pivot table. .EXAMPLE - Get-WmiObject -Class win32_logicaldisk | select -Property DeviceId,VolumeName, Size,Freespace | - Export-Excel -Path "$env:computerName.xlsx" -WorkSheetname Volumes -NumberFormat "0,000" - Get-NetAdapter | Select-Object Name,InterfaceDescription,MacAddress,LinkSpeed | - Export-Excel -Path "$env:COMPUTERNAME.xlsx" -WorkSheetname NetAdapter - Join-Worksheet -Path "$env:COMPUTERNAME.xlsx" -WorkSheetName Summary -Title "Summary" -TitleBold -TitleSize 22 -NoHeader -LabelBlocks -AutoSize -HideSource -show + > + PS> foreach ($computerName in @('Server1', 'Server2', 'Server3', 'Server4')) { + Get-Service -ComputerName $computerName | Select-Object -Property Status, Name, DisplayName, StartType | + Export-Excel -Path .\test.xlsx -WorkSheetname $computerName -AutoSize + } + $ptDef =New-PivotTableDefinition -PivotTableName "Pivot1" -SourceWorkSheet "Combined" -PivotRows "Status" -PivotFilter "MachineName" -PivotData @{Status='Count'} -IncludePivotChart -ChartType BarClustered3D + Join-Worksheet -Path .\test.xlsx -WorkSheetName combined -FromLabel "MachineName" -HideSource -AutoSize -FreezeTopRow -BoldTopRow -PivotTableDefinition $pt -Show - 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 foreach command gets the services running on four servers and exports each to its own page in Test.xlsx. + $PtDef= creates a defintion for a single Pivot table. + The Join-Worksheet command uses the same file and merges the results onto a sheet named "Combined". It sets a column header of "Machinename", + this column will contain the name of the sheet the data was copied from; after copying the data to the sheet "combined", the other sheets will be hidden. + Join-Worksheet finishes by calling export-Excel to AutoSize cells, freeze the top row and make it bold and add the Pivot table. + + .EXAMPLE + > + PS> Get-WmiObject -Class win32_logicaldisk | select -Property DeviceId,VolumeName, Size,Freespace | + Export-Excel -Path "$env:computerName.xlsx" -WorkSheetname Volumes -NumberFormat "0,000" + Get-NetAdapter | Select-Object Name,InterfaceDescription,MacAddress,LinkSpeed | + Export-Excel -Path "$env:COMPUTERNAME.xlsx" -WorkSheetname NetAdapter + Join-Worksheet -Path "$env:COMPUTERNAME.xlsx" -WorkSheetName Summary -Title "Summary" -TitleBold -TitleSize 22 -NoHeader -LabelBlocks -AutoSize -HideSource -show + + The first two commands 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 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 +89,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 +113,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..ede7351 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)] @@ -255,25 +256,25 @@ $ws = $xl.Workbook.Worksheets[$OutputSheetName] for ($i = 0; $i -lt $expandedDiff.Count; $i++ ) { if ( $expandedDiff[$i].side -ne "==" ) { - Set-Format -WorkSheet $ws -Range ("A" + ($i + 2 )) -FontColor $KeyFontColor + Set-ExcelRange -WorkSheet $ws -Range ("A" + ($i + 2 )) -FontColor $KeyFontColor } elseif ( $HideEqual ) {$ws.row($i+2).hidden = $true } if ( $expandedDiff[$i].side -eq "<>" ) { $range = $ws.Dimension -replace "\d+", ($i + 2 ) - Set-Format -WorkSheet $ws -Range $range -BackgroundColor $ChangeBackgroundColor + Set-ExcelRange -WorkSheet $ws -Range $range -BackgroundColor $ChangeBackgroundColor } elseif ( $expandedDiff[$i].side -eq "<=" ) { $rangeR1C1 = "R[{0}]C[1]:R[{0}]C[{1}]" -f ($i + 2 ) , $lastRefColNo $range = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1($rangeR1C1,0,0) - Set-Format -WorkSheet $ws -Range $range -BackgroundColor $DeleteBackgroundColor + Set-ExcelRange -WorkSheet $ws -Range $range -BackgroundColor $DeleteBackgroundColor } elseif ( $expandedDiff[$i].side -eq "=>" ) { if ($propList.count -gt 1) { $rangeR1C1 = "R[{0}]C[{1}]:R[{0}]C[{2}]" -f ($i + 2 ) , $FirstDiffColNo , $lastDiffColNo $range = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1($rangeR1C1,0,0) - Set-Format -WorkSheet $ws -Range $range -BackgroundColor $AddBackgroundColor + Set-ExcelRange -WorkSheet $ws -Range $range -BackgroundColor $AddBackgroundColor } - Set-Format -WorkSheet $ws -Range ("A" + ($i + 2 )) -BackgroundColor $AddBackgroundColor + Set-ExcelRange -WorkSheet $ws -Range ("A" + ($i + 2 )) -BackgroundColor $AddBackgroundColor } } Close-ExcelPackage -ExcelPackage $xl -Show:$Show @@ -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. @@ -433,7 +434,7 @@ Function Merge-MultipleSheets { $columnNo = $cell.start.Column -1 $cellAddr = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R1C$columnNo",1,$columnNo) while ($sheet.cells[$cellAddr].value -match $prefix) { - $condFormattingParams = @{RuleType='Expression'; BackgroundPattern='None'; WorkSheet=$sheet; Range=$([OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[1]C[$columnNo]:R[1048576]C[$columnNo]",0,0)) } + $condFormattingParams = @{RuleType='Expression'; BackgroundPattern='Solid'; WorkSheet=$sheet; Range=$([OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[1]C[$columnNo]:R[1048576]C[$columnNo]",0,0)) } Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Added"' ) -BackgroundColor $AddBackgroundColor Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Changed"') -BackgroundColor $ChangeBackgroundColor Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Removed"') -BackgroundColor $DeleteBackgroundColor diff --git a/New-ConditionalFormattingIconSet.ps1 b/New-ConditionalFormattingIconSet.ps1 index c355150..4d903a1 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 a842245..cdd4810 100644 --- a/New-ConditionalText.ps1 +++ b/New-ConditionalText.ps1 @@ -1,35 +1,75 @@ 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 secind 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 -ConditionalText $ct,$ct2 -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)] + [Alias("ConditionValue")] $Text, + [Alias("ForeGroundColor")] [System.Drawing.Color]$ConditionalTextColor="DarkRed", [System.Drawing.Color]$BackgroundColor="LightPink", [String]$Range, [OfficeOpenXml.Style.ExcelFillStyle]$PatternType=[OfficeOpenXml.Style.ExcelFillStyle]::Solid, [ValidateSet( - "LessThan","LessThanOrEqual","GreaterThan","GreaterThanOrEqual", - "NotEqual","Equal","ContainsText","NotContainsText","BeginsWith","EndsWith", - "Last7Days","LastMonth","LastWeek", - "NextMonth","NextWeek", - "ThisMonth","ThisWeek", - "Today","Tomorrow","Yesterday", - "DuplicateValues", - "AboveOrEqualAverage","BelowAverage","AboveAverage", - "Top", "TopPercent", "ContainsBlanks" + "LessThan", "LessThanOrEqual", "GreaterThan", "GreaterThanOrEqual", + "Equal", "NotEqual", + "Top", "TopPercent", "Bottom", "BottomPercent", + "ContainsText", "NotContainsText", "BeginsWith", "EndsWith", + "ContainsBlanks", "NotContainsBlanks", "ContainsErrors", "NotContainsErrors", + "DuplicateValues", "UniqueValues", + "Tomorrow", "Today", "Yesterday", "Last7Days", + "NextWeek", "ThisWeek", "LastWeek", + "NextMonth", "ThisMonth", "LastMonth", + "AboveAverage", "AboveOrEqualAverage", "BelowAverage", "BelowOrEqualAverage" )] + [Alias("RuleType")] $ConditionalType="ContainsText" ) $obj = [PSCustomObject]@{ Text = $Text ConditionalTextColor = $ConditionalTextColor - ConditionalType = $ConditionalType - PatternType = $PatternType + ConditionalType = $ConditionalType + PatternType = $PatternType Range = $Range - BackgroundColor = $BackgroundColor + BackgroundColor = $BackgroundColor } $obj.pstypenames.Clear() $obj.pstypenames.Add("ConditionalText") - $obj + $obj } \ No newline at end of file diff --git a/New-ExcelChart.ps1 b/New-ExcelChart.ps1 index 841151c..c6eb6c6 100644 --- a/New-ExcelChart.ps1 +++ b/New-ExcelChart.ps1 @@ -1,6 +1,99 @@ function New-ExcelChartDefinition { - [cmdletbinding()] + <# + .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 + > + PS> $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( $Title = "Chart Title", $Header, @@ -13,8 +106,9 @@ function New-ExcelChartDefinition { $RowOffSetPixels = 10, $Column = 6, $ColumnOffSetPixels = 5, + [OfficeOpenXml.Drawing.Chart.eLegendPosition]$LegendPostion, $LegendSize, - [Switch]$legendBold, + [Switch]$LegendBold, [Switch]$NoLegend, [Switch]$ShowCategory, [Switch]$ShowPercent, @@ -53,12 +147,15 @@ function New-ExcelChartDefinition { RowOffSetPixels = $RowOffSetPixels Column = $Column ColumnOffSetPixels = $ColumnOffSetPixels + LegendPostion = $LegendPostion + LegendSize = $LegendSize + Legendbold = $LegendBold NoLegend = $NoLegend -as [Boolean] ShowCategory = $ShowCategory -as [Boolean] ShowPercent = $ShowPercent -as [Boolean] + SeriesHeader = $SeriesHeader TitleBold = $TitleBold -as [Boolean] TitleSize = $TitleSize - SeriesHeader = $SeriesHeader XAxisTitleText = $XAxisTitleText XAxisTitleBold = $XAxisTitleBold -as [Boolean] XAxisTitleSize = $XAxisTitleSize @@ -78,4 +175,264 @@ function New-ExcelChartDefinition { YMinValue = $YMinValue YAxisPosition = $YAxisPosition } -} \ No newline at end of file +} + +function Add-ExcelChart { + <# + .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 + > + PS> $Excel = ConvertFrom-Csv @" + Product, City, Sales + Apple, London , 300 + Orange, London , 400 + Banana, London , 300 + Orange, Paris, 600 + Banana, Paris, 300 + Apple, New York, 1200 + "@ | Export-Excel -Path test.xlsx -PassThru + Add-ExcelChart -Worksheet $Excel.Workbook.Worksheets[1] -ChartType "Doughnut" -XRange "A2:B7" -YRange "C2:C7" -width 500 + Close-ExcelPackage -Show $Excel + + The first command expands a multi-line string into 6 rows of data which is exported to new Excel file; leaving an ExcelPackage object in $excel + The second command adds a chart - the cell ranges are explitly specified. Note the at the XRange (labels) is TWO columns wide and the chart will + combine the name of the product and the name of the City to create the table. + The width of the chart is set explictly, the default legend is used and there is no Chart title. + .EXAMPLE + > + PS> $Excel = Invoke-Sum (Get-Process) Company Handles, PM, VirtualMemorySize | Export-Excel $path -AutoSize -ExcelChartDefinition $c -AutoNameRange -PassThru + Add-ExcelChart -Worksheet $Excel.Workbook.Worksheets[1] -Title "VM use" -ChartType PieExploded3D -XRange "Name" -YRange "VirtualMemorySize" -NoLegend -ShowCategory + Close-ExcelPackage $Excel -Show + + The first line exports information and creates named ranges for each column. + The Second line uses the ranges to specify a chart - the labels come from the range "Name" and the data from the range "VirtualMemorySize" + The chart is specified as a 3D exploded PIE chart, with a title of "VM Use" and instead of a legend the the pie slices are identified with a label. + .EXAMPLE + > + PS> $Excel = Invoke-Sum (Get-Process) Company Handles, PM, VirtualMemorySize | Export-Excel test.xlsx -TableName Processes -PassThru + Add-ExcelChart -Worksheet $Excel.Workbook.Worksheets[1] -Title Stats -ChartType LineMarkersStacked -XRange "Processes[Name]" -YRange "Processes[PM]", "Processes[VirtualMemorySize]" -SeriesHeader 'PM', 'VMSize' + Close-ExcelPackage $Excel -Show + + The first line exports information to a table in new file; and captures the excel Package object in $Excel + The second line creates a chart on the first page of the work sheet, using the notation "TableName[ColumnnName]" to refer to the data, + the labels come Name column in the table, and the data series from its PM and VirtualMemorySize columns. The display names for these in the header are set to PM and VMSize + .EXAMPLE + > + PS> $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 + Close-ExcelPackage $Excel -Show + + 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( + [Parameter(ParameterSetName='Workshet',Mandatory=$true)] + [OfficeOpenXml.ExcelWorksheet]$Worksheet, + [Parameter(ParameterSetName='PivotTable',Mandatory=$true)] + [OfficeOpenXml.Table.PivotTable.ExcelPivotTable]$PivotTable , + [String]$Title, + #$Header, Not used but referenced previously + [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = "ColumnStacked", + $XRange, + $YRange, + [int]$Width = 500, + [int]$Height = 350, + [int]$Row = 0, + [int]$RowOffSetPixels = 10, + [int]$Column = 6, + [int]$ColumnOffSetPixels = 5, + [OfficeOpenXml.Drawing.Chart.eLegendPosition]$LegendPostion, + $LegendSize, + [Switch]$LegendBold, + [Switch]$NoLegend, + [Switch]$ShowCategory, + [Switch]$ShowPercent, + $SeriesHeader, + [Switch]$TitleBold, + [Int]$TitleSize , + [String]$XAxisTitleText, + [Switch]$XAxisTitleBold, + $XAxisTitleSize , + [string]$XAxisNumberformat, + $XMajorUnit, + $XMinorUnit, + $XMaxValue, + $XMinValue, + [OfficeOpenXml.Drawing.Chart.eAxisPosition]$XAxisPosition , + [String]$YAxisTitleText, + [Switch]$YAxisTitleBold, + $YAxisTitleSize, + [string]$YAxisNumberformat, + $YMajorUnit, + $YMinorUnit, + $YMaxValue, + $YMinValue, + [OfficeOpenXml.Drawing.Chart.eAxisPosition]$YAxisPosition, + [Switch]$PassThru + ) + try { + if ($PivotTable) { + $Worksheet = $PivotTable.WorkSheet + $chart = $Worksheet.Drawings.AddChart(("Chart" + $PivotTable.Name ),$ChartType,$PivotTable) + } + else { + $ChartName = 'Chart' + (Split-Path -Leaf ([System.IO.path]::GetTempFileName())) -replace 'tmp|\.', '' + $chart = $Worksheet.Drawings.AddChart($ChartName, $ChartType) + $chartDefCount = @($YRange).Count + if ($chartDefCount -eq 1) { + $Series = $chart.Series.Add($YRange, $XRange) + if ($SeriesHeader) { $Series.Header = $SeriesHeader} + else { $Series.Header = 'Series 1'} + } + else { + for ($idx = 0; $idx -lt $chartDefCount; $idx += 1) { + $Series = $chart.Series.Add($YRange[$idx], $XRange) + if ($SeriesHeader.Count -gt 0) { $Series.Header = $SeriesHeader[$idx] } + else { $Series.Header = "Series $($idx)"} + } + } + } + if ($Title) { + $chart.Title.Text = $Title + if ($TitleBold) {$chart.Title.Font.Bold = $true} + if ($TitleSize) {$chart.Title.Font.Size = $TitleSize} + } + if ($NoLegend) { $chart.Legend.Remove() } + else { + if ($LegendPostion) {$Chart.Legend.Position = $LegendPostion} + if ($LegendSize) {$chart.Legend.Font.Size = $LegendSize} + if ($legendBold) {$chart.Legend.Font.Bold = $true} + } + + if ($XAxisTitleText) { + $chart.XAxis.Title.Text = $XAxisTitleText + if ($XAxisTitleBold) {$chart.XAxis.Title.Font.Bold = $true} + if ($XAxisTitleSize) {$chart.XAxis.Title.Font.Size = $XAxisTitleSize} + } + if ($XAxisPosition) {Write-Warning "X Axis position is not being set propertly at the moment, parameter ignored" } + #$chart.ChartXml.chartSpace.chart.plotArea.catAx.axPos.val = $XAxisPosition.ToString().substring(0,1)} + if ($XMajorUnit) {$chart.XAxis.MajorUnit = $XMajorUnit} + if ($XMinorUnit) {$chart.XAxis.MinorUnit = $XMinorUnit} + if ($null -ne $XMinValue) {$chart.XAxis.MinValue = $XMinValue} + if ($null -ne $XMaxValue) {$chart.XAxis.MaxValue = $XMaxValue} + if ($XAxisNumberformat) {$chart.XAxis.Format = (Expand-NumberFormat $XAxisNumberformat)} + + if ($YAxisTitleText) { + $chart.YAxis.Title.Text = $YAxisTitleText + if ($YAxisTitleBold) {$chart.YAxis.Title.Font.Bold = $true} + if ($YAxisTitleSize) {$chart.YAxis.Title.Font.Size = $YAxisTitleSize} + } + if ($YAxisPosition) {Write-Warning "Y Axis position is not being set propertly at the moment, parameter ignored" } + #$chart.ChartXml.chartSpace.chart.plotArea.valAx.axPos.val= $YAxisPosition.ToString().substring(0,1)} + if ($YMajorUnit) {$chart.YAxis.MajorUnit = $YMajorUnit} + if ($YMinorUnit) {$chart.YAxis.MinorUnit = $YMinorUnit} + if ($null -ne $YMinValue){$chart.YAxis.MinValue = $YMinValue} + if ($null -ne $YMaxValue){$chart.YAxis.MaxValue = $YMaxValue} + if ($YAxisNumberformat) {$chart.YAxis.Format = (Expand-NumberFormat $YAxisNumberformat)} + if ($null -ne $chart.Datalabel) { + $chart.Datalabel.ShowCategory = [boolean]$ShowCategory + $chart.Datalabel.ShowPercent = [boolean]$ShowPercent + } + + $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..c2bb77b 100644 --- a/Open-ExcelPackage.ps1 +++ b/Open-ExcelPackage.ps1 @@ -1,14 +1,28 @@ Function Open-ExcelPackage { <# .Synopsis - Returns an Excel Package Object with for the specified XLSX ile + Returns an Excel Package Object for the specified XLSX file +.Description + Import-Excel and Export-Excel open an Excel file, carry out their tasks and close it again. + Sometimes it is necessary to open a file and do other work on it. Open-Excel package allows the file to be opened for these tasks. + It takes a KillExcel switch to make sure Excel is not holding the file open; a password parameter for existing protected files, + and a create switch to set-up a new file if no file already exists. .Example - $excel = Open-ExcelPackage -path $xlPath + > + PS> $excel = Open-ExcelPackage -Path "$env:TEMP\test99.xlsx" -Create + $ws = Add-WorkSheet -ExcelPackage $excel + + This will create a new file in the temp folder if it doesn't already exist. It then adds a worksheet - + because no name is specified it will use the default name of "Sheet1" +.Example + > + PS> $excel = Open-ExcelPackage -path "$xlPath" -Password $password $sheet1 = $excel.Workbook.Worksheets["sheet1"] - Set-Format -Address $sheet1.Cells["E1:S1048576"], $sheet1.Cells["V1:V1048576"] -NFormat ([cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern) + Set-ExcelRange -Range $sheet1.Cells["E1:S1048576"], $sheet1.Cells["V1:V1048576"] -NFormat ([cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern) Close-ExcelPackage $excel -Show - This will open the file at $xlPath, select sheet1 apply formatting to two blocks of the sheet and save the package, and launch it in Excel. + This will open the password protected file at $xlPath using the password stored in $Password. + Sheet1 is selected and formatting applied to two blocks of the sheet; then the file is and saved and loaded into Excel. #> [CmdLetBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")] @@ -18,6 +32,7 @@ [Parameter(Mandatory=$true)]$Path, #If specified, any running instances of Excel will be terminated before opening the file. [switch]$KillExcel, + #The password for a protected worksheet, as a [normal] string (not a secure string.) [String]$Password, #By default open only opens an existing file; -Create instructs it to create a new file if required. [switch]$Create @@ -40,29 +55,47 @@ New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path } elseif (Test-Path -Path $path) { - if ($Password) {New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path , $Password } - else {New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path } + if ($Password) {$pkgobj = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path , $Password } + else {$pkgobj = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path } + if ($pkgobj) { + foreach ($w in $pkgobj.Workbook.Worksheets) { + $sb = [scriptblock]::Create(('$this.workbook.Worksheets["{0}"]' -f $w.name)) + Add-Member -InputObject $pkgobj -MemberType ScriptProperty -Name $w.name -Value $sb + } + return $pkgobj + } } else {Write-Warning "Could not find $path" } } 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 new file mode 100644 index 0000000..2643099 --- /dev/null +++ b/PivotTable.ps1 @@ -0,0 +1,338 @@ +function Add-PivotTable { + <# + .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 + > + PS> $excel = Get-Service | Export-Excel -Path test.xlsx -WorksheetName Services -PassThru -AutoSize -DisplayPropertySet -TableName ServiceTable -Title "Services on $Env:COMPUTERNAME" + Add-PivotTable -ExcelPackage $excel -PivotTableName ServiceSummary -SourceRange $excel.Workbook.Worksheets[1].Tables[0].Address -PivotRows Status -PivotData Name -NoTotalsInPivot -Activate + Close-ExcelPackage $excel -Show + + This exports data to new workbook and creates a table with the data in. + The Pivot table is added on its own page, the table created in the first command is used as the source for the PivotTable; which counts the service names in for each Status. At the end the Pivot page is made active. + .Example + > + PS> $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 + #> + [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, + #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. + [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 { + try { + if (-not $ExcelPackage) {Write-Warning -message "This combination of Parameters needs to include the ExcelPackage." ; return } + [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"]} + } + catch {throw "Could not create the sheet for the Pivot table. $_" } + } + #if the pivot doesn't exist, create it. + if (-not $wsPivot) {throw "There was a problem getting the worksheet for the pivot table"} + if (-not $wsPivot.PivotTables[$pivotTableName] ) { + try { + #Accept a string or a worksheet object as $SourceWorksheet - we don't need a worksheet if we have a Rangebase . + if ( $SourceWorkSheet -is [string]) { + $SourceWorkSheet = $ExcelPackage.Workbook.Worksheets.where( {$_.name -Like $SourceWorkSheet})[0] + } + elseif ( $SourceWorkSheet -is [int] ) { + $SourceWorkSheet = $ExcelPackage.Workbook.Worksheets[$SourceWorkSheet] + } + if ( $SourceRange -is [OfficeOpenXml.Table.ExcelTable]) {$SourceRange = $SourceRange.Address } + if ( $sourceRange -is [OfficeOpenXml.ExcelRange] -or + $SourceRange -is [OfficeOpenXml.ExcelAddress]) { + $pivotTable = $wsPivot.PivotTables.Add($Address, $SourceRange, $pivotTableName) + } + elseif (-not $SourceRange) { + $pivotTable = $wsPivot.PivotTables.Add($Address, $SourceWorkSheet.cells[$SourceWorkSheet.Dimension.Address], $pivotTableName) + } + elseif ($SourceWorkSheet -isnot [OfficeOpenXml.ExcelWorksheet] ) { + Write-Warning -Message "Could not find source Worksheet for pivot-table '$pivotTableName'." ; return + } + elseif ( $SourceRange -is [String] -or $SourceRange -is [OfficeOpenXml.ExcelAddress]) { + $pivotTable = $wsPivot.PivotTables.Add($Address,$SourceWorkSheet.Cells[$SourceRange], $pivotTableName) + } + else {Write-warning "Could not create a pivot table with the Source Range provided."; return} + 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"]) { + if ($PivotChartDefinition -is [System.Management.Automation.PSCustomObject]) { + $params = @{PivotTable= $pivotTable } + $PivotChartDefinition.PSObject.Properties | ForEach-Object {if ( $null -ne $_.value) {$params[$_.name] = $_.value}} + Add-ExcelChart @params + } + elseif ($PivotChartDefinition -is [hashtable] -or $PivotChartDefinition -is[System.Collections.Specialized.OrderedDictionary]) { + Add-ExcelChart -PivotTable $pivotTable @PivotChartDefinition + } + } + 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 + > + PS> $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, + #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. + [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. + #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 , + #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} +} diff --git a/README.md b/README.md index 1c810fd..d05e6ab 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,23 @@ Install-Module ImportExcel -scope CurrentUser ```PowerShell Install-Module ImportExcel ``` -# New to Aug 16th +- Help improvements and tidying up of examples and extra examples +- Open-Excel Package and Add-Worksheet now add worksheets as script properties so `$Excel = Open-ExcelPackage -path test.xlsx ; $excel.sheet1` will return the sheet named "sheet1" `$Excel.SheetName` is a script property which is defined as `$this.workbook.worksheets["Sheetname"]` +- Renamed Set-Column to `Set-ExcelColumn`, Set-Row to `Set-ExcelRow`, and Set-Format, to `Set-ExcelRange`. Added aliases so the old names still work. +- `Set-ExcelRange` (or set-Format) used "Address" and "Range" incorrectly. There is now a single parameter `-Range`, with an alias of "Address". If the worksheet parameter is present, the function accepts a string specifying cells ("A1:Z10") or a the name of range. Withouth the worksheet it accepts an object representing a named range or a Table; or a tables's address, or part of the worksheet.cells collection. +- `Add-ConditionalFormatting`: Used "address" correctly, and it will accept ranges in the address parameter (range is now an alias for address). It now wraps conditional value strings in quotes when needed (for = <= >= operations string needs to be in double quotes see issue #424). Parameter intellisense has been improved. There are new parameters: `-StopIfTrue` and `-Priority` and support for using the `-Reverse` parameter with Color-scale rules (issue #430). Booleans in the sheet are now supported as the value for a condition. Also brought the two different kinds of condition together inside Export-Excel, and fixed a bug where named-ranges didn't work in some places. In `New-ConditionalText`, more types of conditional format are supported, and the argument completer for -ConditionalTextColor was missing and has been added. +- Improved handling of hyperlinks in `Export-Excel` (see issue #426)s +- `Export-Excel` has better checking of Table and PivotTable names (for uniqueness) and a new test in quick charts that there is suitable data for charting. It also accepts hash tables for chart, pivot table and conditional formatting parameters which are splatted into the functions which add these. +- Moved logic for adding a named-range out of Export-Excel and into a new function named `Add-ExcelName`, and logic for adding a table into a function named `Add-ExcelTable`; this is to make it easier to do these things independently of Export-Excel, but minimize duplication. The Add-ExcelTable command has extra parameters to toggle the options from table tools toolbar (show totals etc) and set options in the totals row. +- Moved PivotTable Functions out of Export-Excel.PS1 into their own file and moved Add-ExcelChart out of Export-Excel.ps1 into New-ExcelChart.ps1 +- Fixed bug in Merge-MultipleSheets where background pattern was set to None, making background color invisible. +- Fixed issues where formatting could be reset when using Export-Excel to manipulate an existing sheet without appending data; this applied to number-formats and tables. +- `Add-PivotTable` has some new parameters `-PassThru` returns the pivot table (e.g. to allow names /sort orders of data series to be tweaked ) `-Address` allows Pivot to be placed on an existing sheet; `-PivotTableStyle` allows a change from "Medium6", `-PivotNumberFormat` formats data cells. It is more flexible about how the source data is specified - copying the range options in Set-ExcelRange. `Add-ExcelChart` is now used for creating PivotCharts, and `-PivotChartDefinition` allows a defintion created with `New-ExcelChartDefinition` to be used when setting up a PivotTable. This opens up all the things that Add-ExcelChart can do without duplicating the parameters on Add-Pivot table and Export-Excel. Definition, TableStyle, Numberformat and ChartDefiniton can be used in `New-PivotTableDefinition` . +- `Add-ExcelChart` now supports -PassThru to return the chart for tweaking after creation; there is now a -PivotTable parameter to allow Add-PivotTable to call the code in Add-ExcelChart. And in `New-ExcelChartDefinition` Legend parameters (for size, bold & position ) are now supported +- ChartDefinition and conditional formatting parameters can now be hashtables - antything that splats Add-ExcelChart or Add-ConditionalFormatting, it should be acceptable as a definition. + + +# What's new in Release 5.2 - Value does not need to be mandatory in Set-Row or Set-Column, also tidied their parameters a little. - Added support for array formulas in Set-Format (it really should be set range now that it sets values, formulas and hyperlinks - that can go on the to-do list ) - Fixed a bug with -Append in Export-Excel which caused it to overwrite the last row if the new data was a simple type. diff --git a/RemoveWorksheet.ps1 b/RemoveWorksheet.ps1 index 0b2c06b..36be4f5 100644 --- a/RemoveWorksheet.ps1 +++ b/RemoveWorksheet.ps1 @@ -9,7 +9,7 @@ $Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage $Path $workSheet = $Excel.Workbook.Worksheets[$WorkSheetName] - + if($workSheet) { if($Excel.Workbook.Worksheets.Count -gt 1) { $Excel.Workbook.Worksheets.Delete($workSheet) @@ -22,14 +22,13 @@ } $Excel.Save() - $Excel.Dispose() + $Excel.Dispose() } -cls -ipmo .\ImportExcel.psd1 -Force +Import-Module .\ImportExcel.psd1 -Force -$names = Get-ExcelSheetInfo C:\Temp\testDelete.xlsx +$names = Get-ExcelSheetInfo C:\Temp\testDelete.xlsx $names | % { Remove-WorkSheet C:\Temp\testDelete.xlsx $_.Name} ##Remove-WorkSheet C:\Temp\testDelete.xlsx sheet6 \ No newline at end of file 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 bd060dc..c5fb42d 100644 --- a/Set-Column.ps1 +++ b/Set-Column.ps1 @@ -1,26 +1,43 @@ -Function Set-Column { +Function Set-ExcelColumn { <# .SYNOPSIS Adds a column to the existing data area in an Excel sheet, fills values and sets formatting .DESCRIPTION - Set-Column takes a value which is either string containing a value or formula or a scriptblock + Set-ExcelColumn takes a value which is either a string containing a value or formula or a scriptblock 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 + A column heading can be specified and the new column can be made a named range. + The column can be formatted in the same operation. + .EXAMPLE + Set-ExcelColumn -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. You can see how currency is interpreted on the local computer with the command + Expand-NumberFormat currency + .EXAMPLE + Set-ExcelColumn -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 + Set-ExcelColumn specifies that Column 7 should have a heading of "WinsToFastLaps" and the data cells should contain =E2/C2 , =E3/C3 etc the data cells should become a named range, which will also be "WinsToFastLaps" the column width will be set automatically + .EXAMPLE + Set-ExcelColumn -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. + The Value parameter 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-ExcelColumn makes it a hyperlink. The column will be autosized to fit the links. #> [cmdletbinding()] + [Alias(" Set-Column")] + [OutputType([OfficeOpenXml.ExcelColumn],[String])] 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 @@ -79,6 +96,8 @@ [float]$Width, #Set the inserted data to be a named range (ignored if header is not specified) [Switch]$AutoNameRange, + #Hide the column + [Switch]$Hide, #If Sepecified returns the range of cells which were affected [Switch]$ReturnRange, #If Specified, return an ExcelPackage object to allow further work to be done on the file. @@ -96,12 +115,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)) { @@ -136,10 +156,11 @@ } $theRange = "$columnName$StartRow`:$columnName$endRow" if ($params.Count) { - Set-Format -WorkSheet $Worksheet -Range $theRange @params + Set-ExcelRange -WorkSheet $Worksheet -Range $theRange @params } #endregion + if ($PSBoundParameters["Hide"]) {$workSheet.Column($Column).Hidden = [bool]$Hide} #return the new data if -passthru was specified. - if ($passThru) { $Worksheet.Column( $Column)} + if ($passThru) { $Worksheet.Column($Column)} elseif ($ReturnRange) { $theRange} } \ No newline at end of file diff --git a/Set-Row.ps1 b/Set-Row.ps1 index 3db7881..b290dcf 100644 --- a/Set-Row.ps1 +++ b/Set-Row.ps1 @@ -1,22 +1,30 @@ -Function Set-Row { +Function Set-ExcelRow { <# .Synopsis - Fills values into a [new] row in an Excel spreadsheet. To format a row without setting values, use Set-Format. + Fills values into a [new] row in an Excel spreadsheet. And sets row formmats. .Description - Set-Row accepts either a Worksheet object or an Excel package object returned by Export-Excel and the name of a sheet, + Set-ExcelRow accepts either a Worksheet object or an Excel package object returned by Export-Excel and the name of a sheet, and inserts the chosen contents into a row of the sheet. The contents can be a constant "42" , a formula or a script block which is converted into a constant or formula. - The first cell of the row can optional be given a heading. + The first cell of the row can optionally be given a heading. .Example - Set-row -Worksheet $ws -Heading Total -Value {"=sum($columnName`2:$columnName$endrow)" } + Set-ExcelRow -Worksheet $ws -Heading Total -Value {"=sum($columnName`2:$columnName$endrow)" } - $Ws contains a worksheet object, and no Row number is specified so Set-Row will select the next row after the end of the data in the sheet + $Ws contains a worksheet object, and no Row number is specified so Set-ExcelRow will select the next row after the end of the data in the sheet The first cell will contain "Total", and each other cell will contain =Sum(xx2:xx99) - where xx is the column name, and 99 is the last row of data. Note the use of `2 to Prevent 2 becoming part of the variable "ColumnName" The script block can use $row, $column, $ColumnName, $startRow/Column $endRow/Column + .Example + Set-ExcelRow -Worksheet $ws -Heading Total -HeadingBold -Value {"=sum($columnName`2:$columnName$endrow)" } -NumberFormat 'Currency' -StartColumn 2 -Bold -BorderTop Double -BorderBottom Thin + + This builds on the previous example, but this time the label "Total" appears in column 2 and the formula fills from column 3 onwards; + the formula and heading are set in bold face, and the formula is formatted for the local currency, + and given a double line border above and single line border below. #> [cmdletbinding()] + [Alias(" Set-Row")] + [OutputType([OfficeOpenXml.ExcelRow],[String])] Param ( #An Excel package object - e.g. from Export-Excel -passthru - requires a sheet name [Parameter(ParameterSetName="Package",Mandatory=$true)] @@ -35,11 +43,25 @@ $Value, #Optional Row heading $Heading , + #Set the heading in bold type + [Switch]$HeadingBold, + #Change the size of the heading type + [Int]$HeadingSize , #Number format to apply to cells e.g. "dd/MM/yyyy HH:mm", "£#,##0.00;[Red]-£#,##0.00", "0.00%" , "##/##" , "0.0E+0" etc [Alias("NFormat")] $NumberFormat, #Style of border to draw around the row [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, #Make text bold; use -Bold:$false to remove bold @@ -54,6 +76,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, @@ -75,6 +98,8 @@ [int]$TextRotation , #Set cells to a fixed hieght [float]$Height, + #Hide the Row + [Switch]$Hide, #If Sepecified returns the range of cells which were affected [Switch]$ReturnRange, #If Specified, return a row object to allow further work to be done @@ -94,8 +119,10 @@ Write-Verbose -Message "Updating Row $Row" #Add a row label if ($Heading) { - $Worksheet.Cells[$Row, $StartColumn].Value = $Heading - $StartColumn ++ + $Worksheet.Cells[$Row, $StartColumn].Value = $Heading + if ($HeadingBold) {$Worksheet.Cells[$Row, $StartColumn].Style.Font.Bold = $true} + if ($HeadingSize) {$Worksheet.Cells[$Row, $StartColumn].Style.Font.Size = $HeadingSize} + $StartColumn ++ } #Fill in the data if ($PSBoundParameters.ContainsKey('Value')) {foreach ($column in ($StartColumn..$endColumn)) { @@ -128,14 +155,16 @@ $params = @{} foreach ($p in @('Underline','Bold','Italic','StrikeThru','FontSize', 'FontShift','NumberFormat','TextRotation', 'WrapText', 'HorizontalAlignment','VerticalAlignment', 'Height', 'FontColor' - 'BorderAround', 'BackgroundColor', 'BackgroundPattern', 'PatternColor')) { + 'BorderAround', 'BorderBottom', 'BorderTop', 'BorderLeft', 'BorderRight', 'BorderColor', + 'BackgroundColor', 'BackgroundPattern', 'PatternColor')) { if ($PSBoundParameters.ContainsKey($p)) {$params[$p] = $PSBoundParameters[$p]} } $theRange = [OfficeOpenXml.ExcelAddress]::New($Row, $StartColumn, $Row, $endColumn) if ($params.Count) { - Set-Format -WorkSheet $Worksheet -Range $theRange @params + Set-ExcelRange -WorkSheet $Worksheet -Range $theRange @params } #endregion + if ($PSBoundParameters["Hide"]) {$workSheet.Row($Row).Hidden = [bool]$Hide} #return the new data if -passthru was specified. if ($passThru) {$Worksheet.Row($Row)} elseif ($ReturnRange) {$theRange} diff --git a/SetFormat.ps1 b/SetFormat.ps1 index 6dcb979..cc65b56 100644 --- a/SetFormat.ps1 +++ b/SetFormat.ps1 @@ -1,34 +1,50 @@ -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 +Function Set-ExcelRange { + <# + .SYNOPSIS + Applies Number, font, alignment and colour formatting, values or formulas to a range of Excel Cells + .DESCRIPTION + Set-ExcelRange was created to set the style elements for a range of cells, this includes autosizing and hiding, setting + font elements (Name, Size, Bold, Italic, Underline & UnderlineStyle and Subscript & SuperScript), font and background colors, + borders, text wrapping, rotation, aliginment within cells, and number format. It was orignally named "Set-ExcelRange" + It has been extended to set Values, Formulas and set ArrayFormulas (sometimes called Ctrl-shift-Enter [CSE] formulas); because of this + the name has become Set-ExcelRange - but the old name of Set-Format is preserved as an alias name may swapped. + .EXAMPLE + $sheet.Column(3) | Set-ExcelRange -HorizontalAlignment Right -NumberFormat "#,###" -AutoFit -#> + Selects column 3 from a sheet object (within a workbook object, which is a child of the ExcelPackage object) and passes it to Set-ExcelRange + which formats as an integer with comma seperated groups, aligns it right, and auto-fits the column to the contents. + .EXAMPLE + Set-ExcelRange -Range $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NumberFormat "#,###" + + Instead of piping the address in this version specifies a block of cells and applies similar formatting + .EXAMPLE + Set-ExcelRange $excel.Workbook.Worksheets[1].Tables["Processes"] -Italic + + This time instead of specifying a range of cells, a table is selected by name and formatted as italic. + #> + [cmdletbinding()] + [Alias("Set-Format")] 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)] - $Address , + [Parameter(ValueFromPipeline = $true,Position=0)] + [Alias("Address")] + $Range , #The worksheet where the format is to be applied - [Parameter(ParameterSetName="SheetAndRange",Mandatory=$True)] [OfficeOpenXml.ExcelWorksheet]$WorkSheet , - #The area of the worksheet where the format is to be applied - [Parameter(ParameterSetName="SheetAndRange",Mandatory=$True)] - [OfficeOpenXml.ExcelAddress]$Range, #Number format to apply to cells e.g. "dd/MM/yyyy HH:mm", "£#,##0.00;[Red]-£#,##0.00", "0.00%" , "##/##" , "0.0E+0" etc [Alias("NFormat")] $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,130 +98,133 @@ #Hide a row or column (not a range); use -Hidden:$false to unhide [Switch]$Hidden ) - 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] } - } - process { - if ($Address -is [Array]) { - [void]$PSBoundParameters.Remove("Address") - $Address | Set-Format @PSBoundParameters + if ($Range -is [Array]) { + [void]$PSBoundParameters.Remove("Range") + $Range | Set-ExcelRange @PSBoundParameters } else { + #We should accept, a worksheet and a name of a range or a cell address; a table; the address of a table; a named range; a row, a column or .Cells[ ] + if ($Range -is [OfficeOpenXml.Table.ExcelTable]) {$Range = $Range.Address} + elseif ($WorkSheet -and ($Range -is [string] -or $Range -is [OfficeOpenXml.ExcelAddress])) { + $Range = $WorkSheet.Cells[$Range] + } + elseif ($Range -is [string]) {Write-Warning -Message "The range pararameter you have specified also needs a worksheet parameter."} + if ($ResetFont) { - $Address.Style.Font.Color.SetColor("Black") - $Address.Style.Font.Bold = $false - $Address.Style.Font.Italic = $false - $Address.Style.Font.UnderLine = $false - $Address.Style.Font.Strike = $false + $Range.Style.Font.Color.SetColor("Black") + $Range.Style.Font.Bold = $false + $Range.Style.Font.Italic = $false + $Range.Style.Font.UnderLine = $false + $Range.Style.Font.Strike = $false + $Range.Style.Font.VerticalAlign = [OfficeOpenXml.Style.ExcelVerticalAlignmentFont]::None } if ($PSBoundParameters.ContainsKey('Underline')) { - $Address.Style.Font.UnderLine = [boolean]$Underline - $Address.Style.Font.UnderLineType = $UnderLineType + $Range.Style.Font.UnderLine = [boolean]$Underline + $Range.Style.Font.UnderLineType = $UnderLineType } if ($PSBoundParameters.ContainsKey('Bold')) { - $Address.Style.Font.Bold = [boolean]$bold + $Range.Style.Font.Bold = [boolean]$bold } if ($PSBoundParameters.ContainsKey('Italic')) { - $Address.Style.Font.Italic = [boolean]$italic + $Range.Style.Font.Italic = [boolean]$italic } if ($PSBoundParameters.ContainsKey('StrikeThru')) { - $Address.Style.Font.Strike = [boolean]$StrikeThru + $Range.Style.Font.Strike = [boolean]$StrikeThru } if ($PSBoundParameters.ContainsKey('FontSize')){ - $Address.Style.Font.Size = $FontSize + $Range.Style.Font.Size = $FontSize } if ($PSBoundParameters.ContainsKey('FontShift')){ - $Address.Style.Font.VerticalAlign = $FontShift + $Range.Style.Font.VerticalAlign = $FontShift } if ($PSBoundParameters.ContainsKey('FontColor')){ - $Address.Style.Font.Color.SetColor( $FontColor) + $Range.Style.Font.Color.SetColor( $FontColor) } if ($PSBoundParameters.ContainsKey('TextRotation')) { - $Address.Style.TextRotation = $TextRotation + $Range.Style.TextRotation = $TextRotation } if ($PSBoundParameters.ContainsKey('WrapText')) { - $Address.Style.WrapText = [boolean]$WrapText + $Range.Style.WrapText = [boolean]$WrapText } if ($PSBoundParameters.ContainsKey('HorizontalAlignment')) { - $Address.Style.HorizontalAlignment = $HorizontalAlignment + $Range.Style.HorizontalAlignment = $HorizontalAlignment } if ($PSBoundParameters.ContainsKey('VerticalAlignment')) { - $Address.Style.VerticalAlignment = $VerticalAlignment + $Range.Style.VerticalAlignment = $VerticalAlignment } if ($PSBoundParameters.ContainsKey('Value')) { - if ($Value -like '=*') {$PSBoundParameters["Formula"] = $Value } + if ($Value -match '^=') {$PSBoundParameters["Formula"] = $Value -replace '^=','' } else { - $Address.Value = $Value - if ($Value -is [datetime]) { $Address.Style.Numberformat.Format = 'm/d/yy h:mm' }# This is not a custom format, but a preset recognized as date and localized. It might be overwritten in a moment - if ($Value -is [timespan]) { $Address.Style.Numberformat.Format = '[h]:mm:ss' } + $Range.Value = $Value + if ($Value -is [datetime]) { $Range.Style.Numberformat.Format = 'm/d/yy h:mm' }# This is not a custom format, but a preset recognized as date and localized. It might be overwritten in a moment + if ($Value -is [timespan]) { $Range.Style.Numberformat.Format = '[h]:mm:ss' } } } if ($PSBoundParameters.ContainsKey('Formula')) { - if ($ArrayFormula) {$Address.CreateArrayFormula(($Formula -replace '^=','')) } - else {$Address.Formula = ($Formula -replace '^=','') } + if ($ArrayFormula) {$Range.CreateArrayFormula(($Formula -replace '^=','')) } + else {$Range.Formula = ($Formula -replace '^=','') } } if ($PSBoundParameters.ContainsKey('NumberFormat')) { - $Address.Style.Numberformat.Format = (Expand-NumberFormat $NumberFormat) + $Range.Style.Numberformat.Format = (Expand-NumberFormat $NumberFormat) } if ($PSBoundParameters.ContainsKey('BorderAround')) { - $Address.Style.Border.BorderAround($BorderAround, $BorderColor) + $Range.Style.Border.BorderAround($BorderAround, $BorderColor) } if ($PSBoundParameters.ContainsKey('BorderBottom')) { - $Address.Style.Border.Bottom.Style=$BorderBottom - $Address.Style.Border.Bottom.Color.SetColor($BorderColor) + $Range.Style.Border.Bottom.Style=$BorderBottom + $Range.Style.Border.Bottom.Color.SetColor($BorderColor) } if ($PSBoundParameters.ContainsKey('BorderTop')) { - $Address.Style.Border.Top.Style=$BorderTop - $Address.Style.Border.Top.Color.SetColor($BorderColor) + $Range.Style.Border.Top.Style=$BorderTop + $Range.Style.Border.Top.Color.SetColor($BorderColor) } if ($PSBoundParameters.ContainsKey('BorderLeft')) { - $Address.Style.Border.Left.Style=$BorderLeft - $Address.Style.Border.Left.Color.SetColor($BorderColor) + $Range.Style.Border.Left.Style=$BorderLeft + $Range.Style.Border.Left.Color.SetColor($BorderColor) } if ($PSBoundParameters.ContainsKey('BorderRight')) { - $Address.Style.Border.Right.Style=$BorderRight - $Address.Style.Border.Right.Color.SetColor($BorderColor) + $Range.Style.Border.Right.Style=$BorderRight + $Range.Style.Border.Right.Color.SetColor($BorderColor) } if ($PSBoundParameters.ContainsKey('BackgroundColor')) { - $Address.Style.Fill.PatternType = $BackgroundPattern - $Address.Style.Fill.BackgroundColor.SetColor($BackgroundColor) + $Range.Style.Fill.PatternType = $BackgroundPattern + $Range.Style.Fill.BackgroundColor.SetColor($BackgroundColor) if ($PatternColor) { - $Address.Style.Fill.PatternColor.SetColor( $PatternColor) + $Range.Style.Fill.PatternColor.SetColor( $PatternColor) } } if ($PSBoundParameters.ContainsKey('Height')) { - if ($Address -is [OfficeOpenXml.ExcelRow] ) {$Address.Height = $Height } - elseif ($Address -is [OfficeOpenXml.ExcelRange] ) { - ($Address.Start.Row)..($Address.Start.Row + $Address.Rows) | - ForEach-Object {$Address.WorkSheet.Row($_).Height = $Height } + if ($Range -is [OfficeOpenXml.ExcelRow] ) {$Range.Height = $Height } + elseif ($Range -is [OfficeOpenXml.ExcelRange] ) { + ($Range.Start.Row)..($Range.Start.Row + $Range.Rows) | + ForEach-Object {$Range.WorkSheet.Row($_).Height = $Height } } - else {Write-Warning -Message ("Can set the height of a row or a range but not a {0} object" -f ($Address.GetType().name)) } + else {Write-Warning -Message ("Can set the height of a row or a range but not a {0} object" -f ($Range.GetType().name)) } } if ($Autosize) { - if ($Address -is [OfficeOpenXml.ExcelColumn]) {$Address.AutoFit() } - elseif ($Address -is [OfficeOpenXml.ExcelRange] ) { - $Address.AutoFitColumns() + if ($Range -is [OfficeOpenXml.ExcelColumn]) {$Range.AutoFit() } + elseif ($Range -is [OfficeOpenXml.ExcelRange] ) { + $Range.AutoFitColumns() } - else {Write-Warning -Message ("Can autofit a column or a range but not a {0} object" -f ($Address.GetType().name)) } + else {Write-Warning -Message ("Can autofit a column or a range but not a {0} object" -f ($Range.GetType().name)) } } elseif ($PSBoundParameters.ContainsKey('Width')) { - if ($Address -is [OfficeOpenXml.ExcelColumn]) {$Address.Width = $Width} - elseif ($Address -is [OfficeOpenXml.ExcelRange] ) { - ($Address.Start.Column)..($Address.Start.Column + $Address.Columns - 1) | + if ($Range -is [OfficeOpenXml.ExcelColumn]) {$Range.Width = $Width} + elseif ($Range -is [OfficeOpenXml.ExcelRange] ) { + ($Range.Start.Column)..($Range.Start.Column + $Range.Columns - 1) | ForEach-Object { #$ws.Column($_).Width = $Width - $Address.Worksheet.Column($_).Width = $Width + $Range.Worksheet.Column($_).Width = $Width } } - else {Write-Warning -Message ("Can set the width of a column or a range but not a {0} object" -f ($Address.GetType().name)) } + else {Write-Warning -Message ("Can set the width of a column or a range but not a {0} object" -f ($Range.GetType().name)) } } if ($PSBoundParameters.ContainsKey('Hidden')) { - if ($Address -is [OfficeOpenXml.ExcelRow] -or - $Address -is [OfficeOpenXml.ExcelColumn] ) {$Address.Hidden = [boolean]$Hidden} - else {Write-Warning -Message ("Can hide a row or a column but not a {0} object" -f ($Address.GetType().name)) } + if ($Range -is [OfficeOpenXml.ExcelRow] -or + $Range -is [OfficeOpenXml.ExcelColumn] ) {$Range.Hidden = [boolean]$Hidden} + else {Write-Warning -Message ("Can hide a row or a column but not a {0} object" -f ($Range.GetType().name)) } } } } @@ -254,9 +273,11 @@ Function NumberFormatCompletion { if (Get-Command -ErrorAction SilentlyContinue -name Register-ArgumentCompleter) { Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName NumberFormat -ScriptBlock $Function:NumberFormatCompletion Register-ArgumentCompleter -CommandName Export-Excel -ParameterName NumberFormat -ScriptBlock $Function:NumberFormatCompletion - Register-ArgumentCompleter -CommandName Set-Format -ParameterName NumberFormat -ScriptBlock $Function:NumberFormatCompletion - Register-ArgumentCompleter -CommandName Set-Column -ParameterName NumberFormat -ScriptBlock $Function:NumberFormatCompletion - Register-ArgumentCompleter -CommandName Set-Row -ParameterName NumberFormat -ScriptBlock $Function:NumberFormatCompletion + Register-ArgumentCompleter -CommandName Set-ExcelRange -ParameterName NumberFormat -ScriptBlock $Function:NumberFormatCompletion + Register-ArgumentCompleter -CommandName Set-ExcelColumn -ParameterName NumberFormat -ScriptBlock $Function:NumberFormatCompletion + Register-ArgumentCompleter -CommandName Set-ExcelRow -ParameterName NumberFormat -ScriptBlock $Function:NumberFormatCompletion + Register-ArgumentCompleter -CommandName Add-PivotTable -ParameterName PivotNumberFormat -ScriptBlock $Function:NumberFormatCompletion + Register-ArgumentCompleter -CommandName New-PivotTableDefinition -ParameterName PivotNumberFormat -ScriptBlock $Function:NumberFormatCompletion Register-ArgumentCompleter -CommandName New-ExcelChartDefinition -ParameterName XAxisNumberformat -ScriptBlock $Function:NumberFormatCompletion Register-ArgumentCompleter -CommandName New-ExcelChartDefinition -ParameterName YAxisNumberformat -ScriptBlock $Function:NumberFormatCompletion Register-ArgumentCompleter -CommandName Add-ExcelChart -ParameterName XAxisNumberformat -ScriptBlock $Function:NumberFormatCompletion @@ -264,7 +285,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 a205129..128fe40 100644 --- a/ToDo.md +++ b/ToDo.md @@ -1,19 +1,4 @@ -- [x] Create an autocomplete for WorkSheetName param on ImportExcel and for number format ; where number format exists Translate "Date", "DateTime", "Currency" -- [x] Support "make worksheet active" -- [X] Add checks for valid worksheet names -- [x] Allow Exclude property to take wildcards -- [ ] 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). -- [X] Test password behaviour in Export-Excel Open & Close-Excel Package -- [X] Test Add PivotTable selecting source sheet by position - -[X] Test UnhideSheet, and Wildcard support for hideSheet -- [X] Test return range support for Set-Row -- [X] Test return range support for Set-Column -- [X] Test Expand-NumberFormat. -- [X] Test Set-Row and Set-column setting values as dates and hyperlinks (only testing Column) -- [X] Increase Test code covereage for Set-Format -- [ ] Increase Test code covereage for import-excel \ No newline at end of file +- [ ] Add help to ConvertToExcelXLSx.ps1, Get-HTMLTable.ps1, GetRange.PS1, GetExcelTable.Ps1, Import-HTML.PS1, New-Psitem.PS1 and Remove-Worksheet.ps1 +- [ ] Add Examples and tests for new "Quick charts" in Export Excel (is it possible to replace examples that use Charting.ps1, GetXYRange.ps1, InferData.PS1 ? ). +- [ ] Increase Test code-covereage for import-excel diff --git a/__tests__/Export-Excel.Tests.ps1 b/__tests__/Export-Excel.Tests.ps1 index 6fcae27..3d51996 100644 --- a/__tests__/Export-Excel.Tests.ps1 +++ b/__tests__/Export-Excel.Tests.ps1 @@ -19,7 +19,7 @@ Describe ExportExcel { } # it "Started Excel to display the file " { - # Get-process -Name Excel, xlim -ErrorAction SilentlyContinue | Should not benullorempty + # Get-process -Name Excel, xlim -ErrorAction SilentlyContinue | Should not beNullOrEmpty # } #Start-Sleep -Seconds 5 ; @@ -28,11 +28,16 @@ Describe ExportExcel { #TODO Need to test opening pre-existing file with no -create switch (and graceful failure when file does not exist) somewhere else $Excel = Open-ExcelPackage -Path $path -KillExcel it "Killed Excel when Open-Excelpackage was told to " { - Get-process -Name Excel, xlim -ErrorAction SilentlyContinue | Should benullorempty + Get-process -Name Excel, xlim -ErrorAction SilentlyContinue | Should beNullOrEmpty } - it "Created 1 worksheet " { + it "Created 1 worksheet, named 'Sheet1' " { $Excel.Workbook.Worksheets.count | Should be 1 + $Excel.Workbook.Worksheets["Sheet1"] | Should not beNullOrEmpty + } + + it "Added a 'Sheet1' property to the Package object " { + $Excel.Sheet1 | Should not beNullOrEmpty } $ws = $Excel.Workbook.Worksheets[1] @@ -147,13 +152,13 @@ Describe ExportExcel { BeforeAll { $path = "$env:temp\test.xlsx" Remove-Item -Path $path -ErrorAction SilentlyContinue - 1..10 | Export-Excel -Path $path -Numberformat 'Number' - 1..10 | Export-Excel -Path $path -Numberformat 'Percentage' -Append - 21..30 | Export-Excel -Path $path -Numberformat 'Currency' -StartColumn 3 - $excel = Open-ExcelPackage -Path $path - $ws = $excel.Workbook.Worksheets[1] + 1..10 | Export-Excel -Path $path -Numberformat 'Number' + 1..10 | Export-Excel -Path $path -Numberformat 'Percentage' -Append + 21..30 | Export-Excel -Path $path -Numberformat 'Currency' -StartColumn 3 + $excel = Open-ExcelPackage -Path $path + $ws = $excel.Workbook.Worksheets[1] } - it "Set the worksheet default number format correctly " { + it "Set the worksheet default number format correctly " { $ws.Cells.Style.Numberformat.Format | Should be "0.00" } it "Set number formats on specific blocks of cells " { @@ -197,6 +202,7 @@ Describe ExportExcel { Link2 = "https://github.com/dfinke/ImportExcel" Link3 = "xl://internal/sheet1!A1" Link4 = "xl://internal/sheet1!C5" + Link5 = (New-Object -TypeName OfficeOpenXml.ExcelHyperLink -ArgumentList "Sheet1!E2" , "Display Text") Process = (Get-Process -Id $PID) TimeSpan = [datetime]::Now.Subtract([datetime]::Today) } | Export-Excel -NoNumberConversion IPAddress, StrLeadZero, StrAltPhone2 -Path $path -Calculate -WarningVariable $warnVar @@ -211,7 +217,7 @@ Describe ExportExcel { $ws = $Excel.Workbook.Worksheets[1] it "Created the worksheet with the expected name, number of rows and number of columns " { $ws.Name | Should be "sheet1" - $ws.Dimension.Columns | Should be 26 + $ws.Dimension.Columns | Should be 27 $ws.Dimension.Rows | Should be 2 } it "Set a date in Cell A2 " { @@ -247,6 +253,8 @@ Describe ExportExcel { $ws.Cells[2, 24].Hyperlink.Scheme | Should be "xl" $ws.Cells[2, 24].Hyperlink.ReferenceAddress | Should be "sheet1!c5" $ws.Cells[2, 24].Hyperlink.Display | Should be "sheet1!c5" + $ws.Cells[2, 25].Hyperlink.ReferenceAddress | Should be "sheet1!E2" + $ws.Cells[2, 25].Hyperlink.Display | Should be "Display Text" } it "Processed thousands according to local settings (Cells H2 and I2) " { if ((Get-Culture).NumberFormat.NumberGroupSeparator -EQ ",") { @@ -271,12 +279,12 @@ Describe ExportExcel { ($ws.Cells[2, 20].Value -is [valuetype] ) | Should be $true } it "Converted a nested object to a string (Y2) " { - $ws.Cells[2, 25].Value | should match '^System\.Diagnostics\.Process\s+\(.*\)$' + $ws.Cells[2, 26].Value | should match '^System\.Diagnostics\.Process\s+\(.*\)$' } it "Processed a timespan object (Z2) " { - $ws.cells[2, 26].Value.ToOADate() | should beGreaterThan 0 - $ws.cells[2, 26].Value.ToOADate() | should beLessThan 1 - $ws.cells[2, 26].Style.Numberformat.Format | should be '[h]:mm:ss' + $ws.cells[2, 27].Value.ToOADate() | should beGreaterThan 0 + $ws.cells[2, 27].Value.ToOADate() | should beLessThan 1 + $ws.cells[2, 27].Style.Numberformat.Format | should be '[h]:mm:ss' } } @@ -450,6 +458,7 @@ Describe ExportExcel { $PTws = $Excel.Workbook.Worksheets["ProcessesPivotTable"] $wCount = $Excel.Workbook.Worksheets.Count it "Added the named sheet and pivot table to the workbook " { + $excel.ProcessesPivotTable | Should not beNullOrEmpty $PTws | Should not beNullOrEmpty $PTws.PivotTables.Count | Should be 1 $Excel.Workbook.Worksheets["Processes"] | Should not beNullOrEmpty @@ -693,16 +702,18 @@ Describe ExportExcel { #Test freezing top row/first column, adding formats and a pivot table - from Add-Pivot table not a specification variable - after the export $excel = Get-Process | Select-Object -Property Name, Company, Handles, CPU, PM, NPM, WS | Export-Excel -Path $path -ClearSheet -WorkSheetname "Processes" -FreezeTopRowFirstColumn -PassThru $sheet = $excel.Workbook.Worksheets["Processes"] - $sheet.Column(1) | Set-Format -Bold -AutoFit - $sheet.Column(2) | Set-Format -Width 29 -WrapText - $sheet.Column(3) | Set-Format -HorizontalAlignment Right -NFormat "#,###" - Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NFormat "#,###" - Set-Format -Address $sheet.Column(4) -HorizontalAlignment Right -NFormat "#,##0.0" -Bold - Set-Format -Address $sheet.Row(1) -Bold -HorizontalAlignment Center + $sheet.Column(1) | Set-ExcelRange -Bold -AutoFit + $sheet.Column(2) | Set-ExcelRange -Width 29 -WrapText + $sheet.Column(3) | Set-ExcelRange -HorizontalAlignment Right -NFormat "#,###" + Set-ExcelRange -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NFormat "#,###" + Set-ExcelRange -Address $sheet.Column(4) -HorizontalAlignment Right -NFormat "#,##0.0" -Bold + Set-ExcelRange -Address $sheet.Row(1) -Bold -HorizontalAlignment Center Add-ConditionalFormatting -WorkSheet $sheet -Range "D2:D1048576" -DataBarColor Red + #test Add-ConditionalFormatting -passthru and using a range (and no worksheet) $rule = Add-ConditionalFormatting -passthru -Address $sheet.cells["C:C"] -RuleType TopPercent -ConditionValue 20 -Bold -StrikeThru Add-ConditionalFormatting -WorkSheet $sheet -Range "G2:G1048576" -RuleType GreaterThan -ConditionValue "104857600" -ForeGroundColor Red -Bold -Italic -Underline -BackgroundColor Beige -BackgroundPattern LightUp -PatternColor Gray - foreach ($c in 5..9) {Set-Format $sheet.Column($c) -AutoFit } + #Test Set-ExcelRange with a column + foreach ($c in 5..9) {Set-ExcelRange $sheet.Column($c) -AutoFit } Add-PivotTable -PivotTableName "PT_Procs" -ExcelPackage $excel -SourceWorkSheet 1 -PivotRows Company -PivotData @{'Name' = 'Count'} -IncludePivotChart -ChartType ColumnClustered -NoLegend Close-ExcelPackage $excel @@ -757,9 +768,21 @@ Describe ExportExcel { Context " # Chart from MultiSeries.ps1 in the Examples\charts Directory" { $path = "$env:TEMP\Test.xlsx" Remove-Item -Path $path -ErrorAction SilentlyContinue + #Test we haven't missed any parameters on New-ChartDefinition which are on add chart or vice versa. + + $ParamChk1 = (get-command Add-ExcelChart ).Parameters.Keys.where({-not (get-command New-ExcelChartDefinition).Parameters.ContainsKey($_) }) | Sort-Object + $ParamChk2 = (get-command New-ExcelChartDefinition).Parameters.Keys.where({-not (get-command Add-ExcelChart ).Parameters.ContainsKey($_) }) + it "Found the same parameters for Add-ExcelChart and New-ExcelChartDefinintion " { + $ParamChk1.count | Should be 3 + $ParamChk1[0] | Should be "PassThru" + $ParamChk1[1] | Should be "PivotTable" + $ParamChk1[2] | Should be "Worksheet" + $ParamChk2.count | Should be 1 + $ParamChk2[0] | Should be "Header" + } #Test Invoke-Sum $data = Invoke-Sum (Get-Process) Company Handles, PM, VirtualMemorySize - it "used Invoke-Sum to create a data set " { + it "Used Invoke-Sum to create a data set " { $data | Should not beNullOrEmpty $data.count | Should beGreaterThan 1 $data[1].Name | Should not beNullOrEmpty diff --git a/__tests__/ExtraLongCmd.tests.ps1 b/__tests__/ExtraLongCmd.tests.ps1 new file mode 100644 index 0000000..9edb3c2 --- /dev/null +++ b/__tests__/ExtraLongCmd.tests.ps1 @@ -0,0 +1,63 @@ + + +$path = "$Env:TEMP\test.xlsx" +remove-item -path $path -ErrorAction SilentlyContinue +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 $path -TableStyle Medium13 -tablename "RawData" -ConditionalFormat @{Range="C2:C7"; DataBarColor="Green"} -ExcelChartDefinition @{ChartType="Doughnut";XRange="A2:B7"; YRange="C2:C7"; width=800; } -PivotTableDefinition @{Sales=@{ + PivotRows="City"; PivotColumns="Product"; PivotData=@{Gross="Sum";Net="Sum"}; PivotNumberFormat="$#,##0.00"; PivotTotals="Both"; PivotTableSyle="Medium12"; Activate=$true + PivotChartDefinition=@{Title="Gross and net by city and product"; ChartType="ColumnClustered"; Column=6; Width=600; Height=360; YMajorUnit=500; YMinorUnit=100; YAxisNumberformat="$#,##0"; LegendPostion="Bottom"}}} + +$excel = Open-ExcelPackage $path +$ws1 = $excel.Workbook.Worksheets[1] +$ws2 = $excel.Workbook.Worksheets[2] +Describe "Creating workbook with a single line" { + Context "Data Page" { + It "Inserted the data and created the table " { + $ws1.Tables[0] | Should not beNullOrEmpty + $ws1.Tables[0].Address.Address | Should be "A1:D7" + $ws1.Tables[0].StyleName | Should be "TableStyleMedium13" + } + It "Applied conditional formatting " { + $ws1.ConditionalFormatting[0] | Should not beNullOrEmpty + $ws1.ConditionalFormatting[0].type.ToString() | Should be "DataBar" + $ws1.ConditionalFormatting[0].Color.G | Should beGreaterThan 100 + $ws1.ConditionalFormatting[0].Color.R | Should beLessThan 100 + $ws1.ConditionalFormatting[0].Address.Address | Should be "C2:C7" + } + It "Added the chart " { + $ws1.Drawings[0] | Should not beNullOrEmpty + $ws1.Drawings[0].ChartType.ToString() | Should be "DoughNut" + $ws1.Drawings[0].Series[0].Series | Should be "'Sheet1'!C2:C7" + } + } + Context "PivotTable" { + it "Created the PivotTable on a new page and made it active " { + $ws2 | Should not beNullOrEmpty + $ws2.PivotTables[0] | Should not beNullOrEmpty + $ws2.PivotTables[0].Fields.Count | Should be 4 + $ws2.PivotTables[0].DataFields[0].Format | Should be "$#,##0.00" + $ws2.PivotTables[0].RowFields[0].Name | Should be "City" + $ws2.PivotTables[0].ColumnFields[0].Name | Should be "Product" + $ws2.PivotTables[0].RowGrandTotals | Should be $true + $ws2.PivotTables[0].ColumGrandTotals | Should be $true #Epplus's mis-spelling of column not mine + $ws2.View.TabSelected | Should be $true + } + it "Created the Pivot Chart " { + $ws2.Drawings[0] | Should not beNullOrEmpty + $ws2.Drawings[0].ChartType.ToString() | Should be ColumnClustered + $ws2.Drawings[0].YAxis.MajorUnit | Should be 500 + $ws2.Drawings[0].YAxis.MinorUnit | Should be 100 + $ws2.Drawings[0].YAxis.Format | Should be "$#,##0" + $ws2.Drawings[0].Legend.Position.ToString() | Should be "Bottom" + } + + } + +} diff --git a/__tests__/First10Races.csv b/__tests__/First10Races.csv new file mode 100644 index 0000000..46b3180 --- /dev/null +++ b/__tests__/First10Races.csv @@ -0,0 +1,101 @@ +Race,Date,FinishPosition,Driver,GridPosition,Team,Points +Australian,25/03/2018,1,Sebastian Vettel,3,Ferrari,25 +Australian,25/03/2018,2,Lewis Hamilton,1,Mercedes,18 +Australian,25/03/2018,3,Kimi Räikkönen,2,Ferrari,15 +Australian,25/03/2018,4,Daniel Ricciardo,8,Red Bull Racing-TAG Heuer,12 +Australian,25/03/2018,5,Fernando Alonso,10,McLaren-Renault,10 +Australian,25/03/2018,6,Max Verstappen,4,Red Bull Racing-TAG Heuer,8 +Australian,25/03/2018,7,Nico Hülkenberg,7,Renault,6 +Australian,25/03/2018,8,Valtteri Bottas,15,Mercedes,4 +Australian,25/03/2018,9,Stoffel Vandoorne,11,McLaren-Renault,2 +Australian,25/03/2018,10,Carlos Sainz,9,Renault,1 +Bahrain,08/04/2018,1,Sebastian Vettel,1,Ferrari,25 +Bahrain,08/04/2018,2,Valtteri Bottas,3,Mercedes,18 +Bahrain,08/04/2018,3,Lewis Hamilton,9,Mercedes,15 +Bahrain,08/04/2018,4,Pierre Gasly,5,STR-Honda,12 +Bahrain,08/04/2018,5,Kevin Magnussen,6,Haas-Ferrari,10 +Bahrain,08/04/2018,6,Nico Hülkenberg,7,Renault,8 +Bahrain,08/04/2018,7,Fernando Alonso,13,McLaren-Renault,6 +Bahrain,08/04/2018,8,Stoffel Vandoorne,14,McLaren-Renault,4 +Bahrain,08/04/2018,9,Marcus Ericsson,17,Sauber-Ferrari,2 +Bahrain,08/04/2018,10,Esteban Ocon,8,Force India-Mercedes,1 +Chinese,15/04/2018,1,Daniel Ricciardo,6,Red Bull Racing-TAG Heuer,25 +Chinese,15/04/2018,2,Valtteri Bottas,3,Mercedes,18 +Chinese,15/04/2018,3,Kimi Räikkönen,2,Ferrari,15 +Chinese,15/04/2018,4,Lewis Hamilton,4,Mercedes,12 +Chinese,15/04/2018,5,Max Verstappen,5,Red Bull Racing-TAG Heuer,10 +Chinese,15/04/2018,6,Nico Hülkenberg,7,Renault,8 +Chinese,15/04/2018,7,Fernando Alonso,13,McLaren-Renault,6 +Chinese,15/04/2018,8,Sebastian Vettel,1,Ferrari,4 +Chinese,15/04/2018,9,Carlos Sainz,9,Renault,2 +Chinese,15/04/2018,10,Kevin Magnussen,11,Haas-Ferrari,1 +Azerbaijan,29/04/2018,1,Lewis Hamilton,2,Mercedes,25 +Azerbaijan,29/04/2018,2,Kimi Räikkönen,6,Ferrari,18 +Azerbaijan,29/04/2018,3,Sergio Pérez,8,Force India-Mercedes,15 +Azerbaijan,29/04/2018,4,Sebastian Vettel,1,Ferrari,12 +Azerbaijan,29/04/2018,5,Carlos Sainz,9,Renault,10 +Azerbaijan,29/04/2018,6,Charles Leclerc,13,Sauber-Ferrari,8 +Azerbaijan,29/04/2018,7,Fernando Alonso,12,McLaren-Renault,6 +Azerbaijan,29/04/2018,8,Lance Stroll,10,Williams-Mercedes,4 +Azerbaijan,29/04/2018,9,Stoffel Vandoorne,16,McLaren-Renault,2 +Azerbaijan,29/04/2018,10,Brendon Hartley,19,STR-Honda,1 +Spanish,13/05/2018,1,Lewis Hamilton,1,Mercedes,25 +Spanish,13/05/2018,2,Valtteri Bottas,2,Mercedes,18 +Spanish,13/05/2018,3,Max Verstappen,5,Red Bull Racing-TAG Heuer,15 +Spanish,13/05/2018,4,Sebastian Vettel,3,Ferrari,12 +Spanish,13/05/2018,5,Daniel Ricciardo,6,Red Bull Racing-TAG Heuer,10 +Spanish,13/05/2018,6,Kevin Magnussen,7,Haas-Ferrari,8 +Spanish,13/05/2018,7,Carlos Sainz,9,Renault,6 +Spanish,13/05/2018,8,Fernando Alonso,8,McLaren-Renault,4 +Spanish,13/05/2018,9,Sergio Pérez,15,Force India-Mercedes,2 +Spanish,13/05/2018,10,Charles Leclerc,14,Sauber-Ferrari,1 +Monaco,27/05/2018,1,Daniel Ricciardo,1,Red Bull Racing-TAG Heuer,25 +Monaco,27/05/2018,2,Sebastian Vettel,2,Ferrari,18 +Monaco,27/05/2018,3,Lewis Hamilton,3,Mercedes,15 +Monaco,27/05/2018,4,Kimi Räikkönen,4,Ferrari,12 +Monaco,27/05/2018,5,Valtteri Bottas,5,Mercedes,10 +Monaco,27/05/2018,6,Esteban Ocon,6,Force India-Mercedes,8 +Monaco,27/05/2018,7,Pierre Gasly,10,STR-Honda,6 +Monaco,27/05/2018,8,Nico Hülkenberg,11,Renault,4 +Monaco,27/05/2018,9,Max Verstappen,20,Red Bull Racing-TAG Heuer,2 +Monaco,27/05/2018,10,Carlos Sainz,8,Renault,1 +Canadian,10/06/2018,1,Sebastian Vettel,1,Ferrari,25 +Canadian,10/06/2018,2,Valtteri Bottas,2,Mercedes,18 +Canadian,10/06/2018,3,Max Verstappen,3,Red Bull Racing-TAG Heuer,15 +Canadian,10/06/2018,4,Daniel Ricciardo,6,Red Bull Racing-TAG Heuer,12 +Canadian,10/06/2018,5,Lewis Hamilton,4,Mercedes,10 +Canadian,10/06/2018,6,Kimi Räikkönen,5,Ferrari,8 +Canadian,10/06/2018,7,Nico Hülkenberg,7,Renault,6 +Canadian,10/06/2018,8,Carlos Sainz,9,Renault,4 +Canadian,10/06/2018,9,Esteban Ocon,8,Force India-Mercedes,2 +Canadian,10/06/2018,10,Charles Leclerc,13,Sauber-Ferrari,1 +French,24/06/2018,1,Lewis Hamilton,1,Mercedes,25 +French,24/06/2018,2,Max Verstappen,4,Red Bull Racing-TAG Heuer,18 +French,24/06/2018,3,Kimi Räikkönen,6,Ferrari,15 +French,24/06/2018,4,Daniel Ricciardo,5,Red Bull Racing-TAG Heuer,12 +French,24/06/2018,5,Sebastian Vettel,3,Ferrari,10 +French,24/06/2018,6,Kevin Magnussen,9,Haas-Ferrari,8 +French,24/06/2018,7,Valtteri Bottas,2,Mercedes,6 +French,24/06/2018,8,Carlos Sainz,7,Renault,4 +French,24/06/2018,9,Nico Hülkenberg,12,Renault,2 +French,24/06/2018,10,Charles Leclerc,8,Sauber-Ferrari,1 +Austrian,01/07/2018,1,Max Verstappen,4,Red Bull Racing-TAG Heuer,25 +Austrian,01/07/2018,2,Kimi Räikkönen,3,Ferrari,18 +Austrian,01/07/2018,3,Sebastian Vettel,6,Ferrari,15 +Austrian,01/07/2018,4,Romain Grosjean,5,Haas-Ferrari,12 +Austrian,01/07/2018,5,Kevin Magnussen,8,Haas-Ferrari,10 +Austrian,01/07/2018,6,Esteban Ocon,11,Force India-Mercedes,8 +Austrian,01/07/2018,7,Sergio Pérez,15,Force India-Mercedes,6 +Austrian,01/07/2018,8,Fernando Alonso,20,McLaren-Renault,4 +Austrian,01/07/2018,9,Charles Leclerc,17,Sauber-Ferrari,2 +Austrian,01/07/2018,10,Marcus Ericsson,18,Sauber-Ferrari,1 +British,08/07/2018,1,Sebastian Vettel,2,Ferrari,25 +British,08/07/2018,2,Lewis Hamilton,1,Mercedes,18 +British,08/07/2018,3,Kimi Räikkönen,3,Ferrari,15 +British,08/07/2018,4,Valtteri Bottas,4,Mercedes,12 +British,08/07/2018,5,Daniel Ricciardo,6,Red Bull Racing-TAG Heuer,10 +British,08/07/2018,6,Nico Hülkenberg,11,Renault,8 +British,08/07/2018,7,Esteban Ocon,10,Force India-Mercedes,6 +British,08/07/2018,8,Fernando Alonso,13,McLaren-Renault,4 +British,08/07/2018,9,Kevin Magnussen,7,Haas-Ferrari,2 +British,08/07/2018,10,Sergio Pérez,12,Force India-Mercedes,1 \ No newline at end of file diff --git a/__tests__/First10Races.tests.ps1 b/__tests__/First10Races.tests.ps1 new file mode 100644 index 0000000..592e077 --- /dev/null +++ b/__tests__/First10Races.tests.ps1 @@ -0,0 +1,101 @@ +$scriptPath = Split-Path -Path $MyInvocation.MyCommand.path -Parent +$dataPath = Join-Path -Path $scriptPath -ChildPath "First10Races.csv" + +Describe "Creating small named ranges with hyperlinks" { + BeforeAll { + $path = "$env:TEMP\Results.xlsx" + Remove-Item -Path $path -ErrorAction SilentlyContinue + #Read race results, and group by race name : export 1 row to get headers, leaving enough rows aboce to put in a link for each race + $results = Import-Csv -Path $dataPath | Group-Object -Property RACE + $topRow = $lastDataRow = 1 + $results.Count + $excel = $results[0].Group[0] | Export-Excel -Path $path -StartRow $TopRow -BoldTopRow -PassThru + + #export each group (race) below the last one, without headers, and create a range for each using the group name (Race) + foreach ($r in $results) { + $excel = $R.Group | Export-Excel -ExcelPackage $excel -NoHeader -StartRow ($lastDataRow +1) -RangeName $R.Name -PassThru -AutoSize + $lastDataRow += $R.Group.Count + } + $worksheet = $excel.Workbook.Worksheets[1] + $columns = $worksheet.Dimension.Columns + + 1..$columns | foreach {Add-ExcelName -Range $worksheet.cells[$topRow,$_,$lastDataRow,$_]} #Test Add-Excel Name on its own (outside Export-Excel) + + $scwarnVar = $null + Set-ExcelColumn -Worksheet $worksheet -StartRow $topRow -Heading "PlacesGained/Lost" ` + -Value "=GridPosition-FinishPosition" -AutoNameRange -WarningVariable scWarnVar -WarningAction SilentlyContinue #Test as many set column options as possible. + $columns ++ + + #create a table which covers all the data. And define a pivot table which uses the same address range. + $table = Add-ExcelTable -PassThru -Range $worksheet.cells[$topRow,1,$lastDataRow,$columns] -TableName "AllResults" -TableStyle Light4 ` + -ShowHeader -ShowFilter -ShowColumnStripes -ShowRowStripes:$false -ShowFirstColumn:$false -ShowLastColumn:$false -ShowTotal:$false #Test Add-ExcelTable outside export-Excel with as many options as possible. + $pt = New-PivotTableDefinition -PivotTableName Analysis -SourceWorkSheet $worksheet -SourceRange $table.address.address -PivotRows Driver -PivotData @{Points="SUM"} -PivotTotals None + + $cf = Add-ConditionalFormatting -Address $worksheet.cells[$topRow,$columns,$lastDataRow,$columns] -ThreeIconsSet Arrows -Passthru #Test using cells[r1,c1,r2,c2] + $cf.Icon2.Type = $cf.Icon3.Type = "Num" + $cf.Icon2.Value = 0 + $cf.Icon3.Value = 1 + Add-ConditionalFormatting -Address $worksheet.cells["FinishPosition"] -RuleType Equal -ConditionValue 1 -ForeGroundColor Purple -Bold -Priority 1 -StopIfTrue #Test Priority and stopIfTrue and using range name + Add-ConditionalFormatting -Address $worksheet.Cells["GridPosition"] -RuleType ThreeColorScale -Reverse #Test Reverse + $ct = New-ConditionalText -Text "Ferrari" + $ct2 = New-ConditionalText -Range $worksheet.Names["FinishPosition"].Address -ConditionalType LessThanOrEqual -Text 3 -ConditionalText Red -Background White #Test new-conditionalText in shortest and longest forms. + #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")} | #Test Exporting Hyperlinks with display property. + Export-Excel -ExcelPackage $excel -AutoSize -PivotTableDefinition $pt -Calculate -ConditionalFormat $ct,$ct2 #Test conditional text rules in conditional format (orignally icon sets only ) + + $excel = Open-ExcelPackage $path + $sheet = $excel.Workbook.Worksheets[1] + $m = $results | measure -sum -Property count + $expectedRows = 1 + $m.count + $m.sum + } + Context "Creating hyperlinks" { + it "Put the data into the sheet and created the expected named ranges " { + $sheet.Dimension.Rows | should be $expectedRows + $sheet.Dimension.Columns | should be $columns + $sheet.Names.Count | should be ($columns + $results.Count) + $sheet.Names[$results[0].Name] | should not benullorEmpty + $sheet.Names[$results[-1].Name] | should not benullorEmpty + } + it "Added hyperlinks to the named ranges " { + $sheet.cells["a1"].Hyperlink.Display | should match $results[0].Name + $sheet.cells["a1"].Hyperlink.ReferenceAddress | should match $results[0].Name + } + } + Context "Adding calculated column" { + It "Populated the cells with the right heading and formulas " { + $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 "GridPosition-FinishPosition" + $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 + } + It "Applied ConditionalFormatting, including StopIfTrue, Priority and Reverse " { + $sheet.ConditionalFormatting[0].Address.Start.Column | should be $columns + $sheet.ConditionalFormatting[0].Address.End.Column | should be $columns + $sheet.ConditionalFormatting[0].Address.End.Row | should be $expectedRows + $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[1].Priority | Should be 1 + $sheet.ConditionalFormatting[1].StopIfTrue | Should be $true + $sheet.ConditionalFormatting[3].LowValue.Color.R | Should begreaterThan 180 + $sheet.ConditionalFormatting[3].LowValue.Color.G | Should beLessThan 128 + $sheet.ConditionalFormatting[3].HighValue.Color.R | Should beLessThan 128 + $sheet.ConditionalFormatting[3].HighValue.Color.G | Should begreaterThan 180 + } + } + Context "Adding a table" { + it "Created a table " { + $sheet.tables[0] | Should not beNullOrEmpty + $sheet.tables[0].Address.Start.Column | should be 1 + $sheet.tables[0].Address.End.Column | should be $columns + $sheet.tables[0].Address.Start.row | should be ($results.Count + 1) + $sheet.Tables[0].Address.End.Row | should be $expectedRows + $sheet.Tables[0].StyleName | should be "TableStyleLight4" + $sheet.Tables[0].ShowColumnStripes | should be $true + $sheet.Tables[0].ShowRowStripes | should not be $true + } + } +} \ No newline at end of file diff --git a/__tests__/Join-Worksheet.tests.ps1 b/__tests__/Join-Worksheet.tests.ps1 index 89d2296..bf4c083 100644 --- a/__tests__/Join-Worksheet.tests.ps1 +++ b/__tests__/Join-Worksheet.tests.ps1 @@ -1,4 +1,4 @@ -$data1 = ConvertFrom-Csv -InputObject @" +$data1 = ConvertFrom-Csv -InputObject @" ID,Product,Quantity,Price,Total 12001,Nails,37,3.99,147.63 12002,Hammer,5,12.10,60.5 @@ -29,33 +29,33 @@ Describe "Join Worksheet" { $data1 | Export-Excel -Path $path -WorkSheetname Oxford $data2 | Export-Excel -Path $path -WorkSheetname Abingdon $data3 | Export-Excel -Path $path -WorkSheetname Banbury - $ptdef = New-PivotTableDefinition -PivotTableName "Summary" -PivotRows "Store" -PivotColumns "Product" -PivotData @{"Total"="SUM"} -IncludePivotChart -ChartTitle "Sales Breakdown" -ChartType ColumnStacked -ChartColumn 10 - Join-Worksheet -Path $path -WorkSheetName "Total" -Clearsheet -FromLabel "Store" -TableName "Summary" -TableStyle Light1 -AutoSize -BoldTopRow -FreezePane 2,1 -Title "Store Sales Summary" -TitleBold -TitleSize 14 -PivotTableDefinition $ptdef + $ptdef = New-PivotTableDefinition -PivotTableName "SummaryPivot" -PivotRows "Store" -PivotColumns "Product" -PivotData @{"Total"="SUM"} -IncludePivotChart -ChartTitle "Sales Breakdown" -ChartType ColumnStacked -ChartColumn 10 + Join-Worksheet -Path $path -WorkSheetName "Total" -Clearsheet -FromLabel "Store" -TableName "SummaryTable" -TableStyle Light1 -AutoSize -BoldTopRow -FreezePane 2,1 -Title "Store Sales Summary" -TitleBold -TitleSize 14 -PivotTableDefinition $ptdef - $excel = Export-Excel -path $path -WorkSheetname Summary -Activate -HideSheet * -UnHideSheet "Total","Summary" -PassThru + $excel = Export-Excel -path $path -WorkSheetname SummaryPivot -Activate -HideSheet * -UnHideSheet "Total","SummaryPivot" -PassThru # Open-ExcelPackage -Path $path $ws = $excel.Workbook.Worksheets["Total"] - $pt = $excel.Workbook.Worksheets["Summary"].pivottables[0] - $pc = $excel.Workbook.Worksheets["Summary"].Drawings[0] + $pt = $excel.Workbook.Worksheets["SummaryPivot"].pivottables[0] + $pc = $excel.Workbook.Worksheets["SummaryPivot"].Drawings[0] } Context "Export-Excel setting spreadsheet visibility" { it "Hid the worksheets " { - $excel.Workbook.Worksheets["Oxford"].Hidden | Should be $true - $excel.Workbook.Worksheets["Banbury"].Hidden | Should be $true - $excel.Workbook.Worksheets["Abingdon"].Hidden | Should be $true + $excel.Workbook.Worksheets["Oxford"].Hidden | Should be 'Hidden' + $excel.Workbook.Worksheets["Banbury"].Hidden | Should be 'Hidden' + $excel.Workbook.Worksheets["Abingdon"].Hidden | Should be 'Hidden' } it "Un-hid two of the worksheets " { - $excel.Workbook.Worksheets["Total"].Hidden | Should be $false - $excel.Workbook.Worksheets["Summary"].Hidden | Should be $false + $excel.Workbook.Worksheets["Total"].Hidden | Should be 'Visible' + $excel.Workbook.Worksheets["SummaryPivot"].Hidden | Should be 'Visible' } it "Activated the correct worksheet " { - $excel.Workbook.worksheets["Summary"].View.TabSelected | Should be $true + $excel.Workbook.worksheets["SummaryPivot"].View.TabSelected | Should be $true $excel.Workbook.worksheets["Total"].View.TabSelected | Should be $false } } - Context "Merging 3 blocks" { + Context "Merging 3 blocks" { it "Created sheet of the right size with a title and a table " { $ws.Dimension.Address | Should be "A1:F16" $ws.Tables[0].Address.Address | Should be "A2:F16" diff --git a/__tests__/RangePassing.ps1 b/__tests__/RangePassing.ps1 new file mode 100644 index 0000000..d71e1b8 --- /dev/null +++ b/__tests__/RangePassing.ps1 @@ -0,0 +1,114 @@ +$path = "$env:temp\test.xlsx" +describe "Consistent passing of ranges." { + Context "Conditional Formatting" { + Remove-Item -path $path -ErrorAction SilentlyContinue + $excel = Get-Service | Export-Excel -Path $path -WorksheetName Services -PassThru -AutoSize -DisplayPropertySet -AutoNameRange -Title "Services on $Env:COMPUTERNAME" + it "accepts named ranges, cells['name'], worksheet + Name, worksheet + column " { + {Add-ConditionalFormatting $excel.Services.Names["Status"] -StrikeThru -RuleType ContainsText -ConditionValue "Stopped" } | Should not throw + $excel.Services.ConditionalFormatting.Count | Should be 1 + {Add-ConditionalFormatting $excel.Services.Cells["Name"] -Italic -RuleType ContainsText -ConditionValue "SVC" } | Should not throw + $excel.Services.ConditionalFormatting.Count | Should be 2 + $warnvar = $null + Add-ConditionalFormatting $excel.Services.Column(3) ` + -underline -RuleType ContainsText -ConditionValue "Windows" -WarningVariable warnvar -WarningAction SilentlyContinue + $warnvar | should not beNullOrEmpty + $excel.Services.ConditionalFormatting.Count | Should be 2 + $warnvar = $null + Add-ConditionalFormatting $excel.Services.Column(3) -WorkSheet $excel.Services` + -underline -RuleType ContainsText -ConditionValue "Windows" -WarningVariable warnvar -WarningAction SilentlyContinue + $warnvar | should beNullOrEmpty + $excel.Services.ConditionalFormatting.Count | Should be 3 + {Add-ConditionalFormatting "Status" -WorkSheet $excel.Services ` + -ForeGroundColor Green -RuleType ContainsText -ConditionValue "Running"} | Should not throw + $excel.Services.ConditionalFormatting.Count | Should be 4 + } + Close-ExcelPackage -NoSave $excel + $excel = Get-Service | Export-Excel -Path $path -WorksheetName Services -PassThru -AutoSize -DisplayPropertySet -TableName servicetable -Title "Services on $Env:COMPUTERNAME" + it "accepts table, table.Address and worksheet + 'C:C' " { + {Add-ConditionalFormatting $excel.Services.Tables[0] ` + -Italic -RuleType ContainsText -ConditionValue "Svc" } | Should not throw + $excel.Services.ConditionalFormatting.Count | Should be 1 + {Add-ConditionalFormatting $excel.Services.Tables["ServiceTable"].Address ` + -Bold -RuleType ContainsText -ConditionValue "windows" } | Should not throw + $excel.Services.ConditionalFormatting.Count | Should be 2 + {Add-ConditionalFormatting -WorkSheet $excel.Services -Address "a:a" ` + -RuleType ContainsText -ConditionValue "stopped" -ForeGroundColor Red } | Should not throw + $excel.Services.ConditionalFormatting.Count | Should be 3 + } + Close-ExcelPackage -NoSave $excel + } + + Context "Formating (Set-ExcelRange or its alias set-Format) " { + it "accepts Named Range, cells['Name'], cells['A1:Z9'], row, Worksheet + 'A1:Z9'" { + $excel = Get-Service | Export-Excel -Path test2.xlsx -WorksheetName Services -PassThru -AutoSize -DisplayPropertySet -RangeName servicerange -Title "Services on $Env:COMPUTERNAME" + {set-format $excel.Services.Names["serviceRange"] -Bold } | Should Not Throw + $excel.Services.cells["B2"].Style.Font.Bold | Should be $true + {Set-ExcelRange -Range $excel.Services.Cells["serviceRange"] -italic:$true } | Should not throw + $excel.Services.cells["C3"].Style.Font.Italic | Should be $true + {set-format $excel.Services.Row(4) -underline -Bold:$false } | Should not throw + $excel.Services.cells["A4"].Style.Font.UnderLine | Should be $true + $excel.Services.cells["A4"].Style.Font.Bold | Should not be $true + {Set-ExcelRange $excel.Services.Cells["A3:B3"] -StrikeThru } | Should not throw + $excel.Services.cells["B3"].Style.Font.Strike | Should be $true + {Set-ExcelRange -WorkSheet $excel.Services -Range "A5:B6" -FontSize 8 } | Should not throw + $excel.Services.cells["A5"].Style.Font.Size | Should be 8 + } + Close-ExcelPackage -NoSave $excel + it "Accepts Table, Table.Address , worksheet + Name, Column," { + $excel = Get-Service | Export-Excel -Path test2.xlsx -WorksheetName Services -PassThru -AutoNameRange -AutoSize -DisplayPropertySet -TableName servicetable -Title "Services on $Env:COMPUTERNAME" + {set-ExcelRange $excel.Services.Tables[0] -Italic } | Should not throw + $excel.Services.cells["C3"].Style.Font.Italic | Should be $true + {set-format $excel.Services.Tables["ServiceTable"].Address -Underline } | Should not throw + $excel.Services.cells["C3"].Style.Font.UnderLine | Should be $true + {Set-ExcelRange -WorkSheet $excel.Services -Range "Name" -Bold } | Should not throw + $excel.Services.cells["B4"].Style.Font.Bold | Should be $true + {$excel.Services.Column(3) | Set-ExcelRange -FontColor red } | Should not throw + $excel.Services.cells["C4"].Style.Font.Color.Rgb | Should be "FFFF0000" + } + Close-ExcelPackage -NoSave $excel + } + + Context "PivotTables" { + it "Accepts Named range, .Cells['Name'], name&Worksheet, cells['A1:Z9'], worksheet&'A1:Z9' "{ + $excel = Get-Service | Export-Excel -Path $path -WorksheetName Services -PassThru -AutoSize -DisplayPropertySet -RangeName servicerange -Title "Services on $Env:COMPUTERNAME" + $ws = $excel.Workbook.Worksheets[1] #can get a worksheet by name or index - starting at 1 + $end = $ws.Dimension.End.Address + #can get a named ranged by name or index - starting at zero + {Add-PivotTable -ExcelPackage $excel -PivotTableName pt0 -SourceRange $ws.Names[0]` + -PivotRows Status -PivotData Name } | Should not throw + $excel.Workbook.Worksheets["pt0"] | Should not beNullOrEmpty + {Add-PivotTable -ExcelPackage $excel -PivotTableName pt1 -SourceRange $ws.Names["servicerange"]` + -PivotRows Status -PivotData Name } | Should not throw + $excel.Workbook.Worksheets["pt1"] | Should not beNullOrEmpty + #Can specify the range for a pivot as NamedRange or Table or TableAddress or Worksheet + "A1:Z10" or worksheet + RangeName, or worksheet.cells["A1:Z10"] or worksheet.cells["RangeName"] + {Add-PivotTable -ExcelPackage $excel -PivotTableName pt2 -SourceRange "servicerange" -SourceWorkSheet $ws ` + -PivotRows Status -PivotData Name } | Should not throw + $excel.Workbook.Worksheets["pt2"] | Should not beNullOrEmpty + {Add-PivotTable -ExcelPackage $excel -PivotTableName pt3 -SourceRange $ws.cells["servicerange"]` + -PivotRows Status -PivotData Name } | Should not throw + $excel.Workbook.Worksheets["pt3"] | Should not beNullOrEmpty + {Add-PivotTable -ExcelPackage $excel -PivotTableName pt4 -SourceRange $ws.cells["A2:$end"]` + -PivotRows Status -PivotData Name } | Should not throw + $excel.Workbook.Worksheets["pt4"] | Should not beNullOrEmpty + {Add-PivotTable -ExcelPackage $excel -PivotTableName pt5 -SourceRange "A2:$end" -SourceWorkSheet $ws ` + -PivotRows Status -PivotData Name } | Should not throw + $excel.Workbook.Worksheets["pt5"] | Should not beNullOrEmpty + Close-ExcelPackage -NoSave $excel + } + it "Accepts Table, Table.Addres " { + $excel = Get-Service | Export-Excel -Path $path -WorksheetName Services -PassThru -AutoSize -DisplayPropertySet -TableName servicetable -Title "Services on $Env:COMPUTERNAME" + $ws = $excel.Workbook.Worksheets["Services"] #can get a worksheet by name or index - starting at 1 + #Can get a table by name or -stating at zero. Can specify the range for a pivot as or Table or TableAddress + {Add-PivotTable -ExcelPackage $excel -PivotTableName pt1 -SourceRange $ws.tables["servicetable"]` + -PivotRows Status -PivotData Name } | Should not throw + $excel.Workbook.Worksheets["pt1"] | Should not beNullOrEmpty + {Add-PivotTable -ExcelPackage $excel -PivotTableName pt2 -SourceRange $ws.tables[0].Address ` + -PivotRows Status -PivotData Name } | Should not throw + $excel.Workbook.Worksheets["pt2"] | Should not beNullOrEmpty + Close-ExcelPackage -NoSave $excel + } + + + + } +} \ No newline at end of file diff --git a/__tests__/Set-Row_Set-Column-SetFormat.tests.ps1 b/__tests__/Set-Row_Set-Column-SetFormat.tests.ps1 index d0583b4..80f1f0d 100644 --- a/__tests__/Set-Row_Set-Column-SetFormat.tests.ps1 +++ b/__tests__/Set-Row_Set-Column-SetFormat.tests.ps1 @@ -37,8 +37,7 @@ Describe "Number format expansion and setting" { Context "Expand-NumberFormat function" { It "Expanded named number formats as expected " { $r = [regex]::Escape([cultureinfo]::CurrentCulture.NumberFormat.CurrencySymbol) - - Expand-NumberFormat 'Currency' | Should match "^[$r\(\)\[\]RED0#\?\-;,.]+$" + Expand-NumberFormat 'Currency' | Should match "^[$r\(\)\[\] RED0#\?\-;,.]+$" Expand-NumberFormat 'Number' | Should be "0.00" Expand-NumberFormat 'Percentage' | Should be "0.00%" Expand-NumberFormat 'Scientific' | Should be "0.00E+00" @@ -57,37 +56,37 @@ Describe "Number format expansion and setting" { $excel = 1..32 | ForEach-Object {$n} | Export-Excel -Path $path -show -WorksheetName s2 -PassThru $ws = $excel.Workbook.Worksheets[1] - Set-Format -WorkSheet $ws -Range "A1" -numberFormat 'General' - Set-Format -WorkSheet $ws -Range "A2" -numberFormat 'Number' - Set-Format -WorkSheet $ws -Range "A3" -numberFormat 'Percentage' - Set-Format -WorkSheet $ws -Range "A4" -numberFormat 'Scientific' - Set-Format -WorkSheet $ws -Range "A5" -numberFormat 'Fraction' - Set-Format -WorkSheet $ws -Range "A6" -numberFormat 'Short Date' - Set-Format -WorkSheet $ws -Range "A7" -numberFormat 'Short Time' - Set-Format -WorkSheet $ws -Range "A8" -numberFormat 'Long Time' - Set-Format -WorkSheet $ws -Range "A9" -numberFormat 'Date-Time' - Set-Format -WorkSheet $ws -Range "A10" -numberFormat 'Currency' - Set-Format -WorkSheet $ws -Range "A11" -numberFormat 'Text' - Set-Format -WorkSheet $ws -Range "A12" -numberFormat 'h:mm AM/PM' - Set-Format -WorkSheet $ws -Range "A13" -numberFormat 'h:mm:ss AM/PM' - Set-Format -WorkSheet $ws -Range "A14" -numberFormat 'mm:ss' - Set-Format -WorkSheet $ws -Range "A15" -numberFormat '[h]:mm:ss' - Set-Format -WorkSheet $ws -Range "A16" -numberFormat 'mmss.0' - Set-Format -WorkSheet $ws -Range "A17" -numberFormat 'd-mmm-yy' - Set-Format -WorkSheet $ws -Range "A18" -numberFormat 'd-mmm' - Set-Format -WorkSheet $ws -Range "A19" -numberFormat 'mmm-yy' - Set-Format -WorkSheet $ws -Range "A20" -numberFormat '0' - Set-Format -WorkSheet $ws -Range "A21" -numberFormat '0.00' - Set-Format -Address $ws.Cells[ "A22"] -NumberFormat '#,##0' - Set-Format -Address $ws.Cells[ "A23"] -NumberFormat '#,##0.00' - Set-Format -Address $ws.Cells[ "A24"] -NumberFormat '#,' - Set-Format -Address $ws.Cells[ "A25"] -NumberFormat '#.0,,' - Set-Format -Address $ws.Cells[ "A26"] -NumberFormat '0%' - Set-Format -Address $ws.Cells[ "A27"] -NumberFormat '0.00%' - Set-Format -Address $ws.Cells[ "A28"] -NumberFormat '0.00E+00' - Set-Format -Address $ws.Cells[ "A29"] -NumberFormat '# ?/?' - Set-Format -Address $ws.Cells[ "A30"] -NumberFormat '# ??/??' - Set-Format -Address $ws.Cells[ "A31"] -NumberFormat '@' + Set-ExcelRange -WorkSheet $ws -Range "A1" -numberFormat 'General' + Set-ExcelRange -WorkSheet $ws -Range "A2" -numberFormat 'Number' + Set-ExcelRange -WorkSheet $ws -Range "A3" -numberFormat 'Percentage' + Set-ExcelRange -WorkSheet $ws -Range "A4" -numberFormat 'Scientific' + Set-ExcelRange -WorkSheet $ws -Range "A5" -numberFormat 'Fraction' + Set-ExcelRange -WorkSheet $ws -Range "A6" -numberFormat 'Short Date' + Set-ExcelRange -WorkSheet $ws -Range "A7" -numberFormat 'Short Time' + Set-ExcelRange -WorkSheet $ws -Range "A8" -numberFormat 'Long Time' + Set-ExcelRange -WorkSheet $ws -Range "A9" -numberFormat 'Date-Time' + Set-ExcelRange -WorkSheet $ws -Range "A10" -numberFormat 'Currency' + Set-ExcelRange -WorkSheet $ws -Range "A11" -numberFormat 'Text' + Set-ExcelRange -WorkSheet $ws -Range "A12" -numberFormat 'h:mm AM/PM' + Set-ExcelRange -WorkSheet $ws -Range "A13" -numberFormat 'h:mm:ss AM/PM' + Set-ExcelRange -WorkSheet $ws -Range "A14" -numberFormat 'mm:ss' + Set-ExcelRange -WorkSheet $ws -Range "A15" -numberFormat '[h]:mm:ss' + Set-ExcelRange -WorkSheet $ws -Range "A16" -numberFormat 'mmss.0' + Set-ExcelRange -WorkSheet $ws -Range "A17" -numberFormat 'd-mmm-yy' + Set-ExcelRange -WorkSheet $ws -Range "A18" -numberFormat 'd-mmm' + Set-ExcelRange -WorkSheet $ws -Range "A19" -numberFormat 'mmm-yy' + Set-ExcelRange -WorkSheet $ws -Range "A20" -numberFormat '0' + Set-ExcelRange -WorkSheet $ws -Range "A21" -numberFormat '0.00' + Set-ExcelRange -Address $ws.Cells[ "A22"] -NumberFormat '#,##0' + Set-ExcelRange -Address $ws.Cells[ "A23"] -NumberFormat '#,##0.00' + Set-ExcelRange -Address $ws.Cells[ "A24"] -NumberFormat '#,' + Set-ExcelRange -Address $ws.Cells[ "A25"] -NumberFormat '#.0,,' + Set-ExcelRange -Address $ws.Cells[ "A26"] -NumberFormat '0%' + Set-ExcelRange -Address $ws.Cells[ "A27"] -NumberFormat '0.00%' + Set-ExcelRange -Address $ws.Cells[ "A28"] -NumberFormat '0.00E+00' + Set-ExcelRange -Address $ws.Cells[ "A29"] -NumberFormat '# ?/?' + Set-ExcelRange -Address $ws.Cells[ "A30"] -NumberFormat '# ??/??' + Set-ExcelRange -Address $ws.Cells[ "A31"] -NumberFormat '@' Close-ExcelPackage -ExcelPackage $excel @@ -130,36 +129,36 @@ Describe "Number format expansion and setting" { } } -Describe "Set-Column, Set-Row and Set Format" { +Describe "Set-ExcelColumn, Set-ExcelRow and Set-ExcelRange" { BeforeAll { Remove-Item -Path $path -ErrorAction SilentlyContinue $excel = $data| Export-Excel -Path $path -AutoNameRange -PassThru $ws = $excel.Workbook.Worksheets["Sheet1"] - $c = Set-Column -PassThru -Worksheet $ws -Heading "Total" -Value "=Quantity*Price" -NumberFormat "£#,###.00" -FontColor Blue -Bold -HorizontalAlignment Right -VerticalAlignment Top - $r = Set-Row -PassThru -Worksheet $ws -StartColumn 3 -BorderAround Thin -Italic -Underline -FontSize 14 -Value {"=sum($columnName`2:$columnName$endrow)" } -VerticalAlignment Bottom - Set-Format -Address $excel.Workbook.Worksheets["Sheet1"].cells["b3"] -HorizontalAlignment Right -VerticalAlignment Center -BorderAround Thick -BorderColor Red -StrikeThru - Set-Format -Address $excel.Workbook.Worksheets["Sheet1"].cells["c3"] -BorderColor Red -BorderTop DashDot -BorderLeft DashDotDot -BorderBottom Dashed -BorderRight Dotted - Set-Format -WorkSheet $ws -Range "E3" -Bold:$false -FontShift Superscript -HorizontalAlignment Left - Set-Format -WorkSheet $ws -Range "E1" -ResetFont -HorizontalAlignment General - Set-Format -Address $ws.cells["E7"] -ResetFont -WrapText -BackgroundColor AliceBlue -BackgroundPattern DarkTrellis -PatternColor Red -NumberFormat "£#,###.00" - Set-Format -Address $ws.Column(1) -Width 0 - Set-Format -Address $ws.Column(2) -AutoFit - Set-Format -Address $ws.Cells["E:E"] -AutoFit - Set-Format -Address $ws.row(5) -Height 0 + $c = Set-ExcelColumn -PassThru -Worksheet $ws -Heading "Total" -Value "=Quantity*Price" -NumberFormat "£#,###.00" -FontColor Blue -Bold -HorizontalAlignment Right -VerticalAlignment Top + $r = Set-ExcelRow -PassThru -Worksheet $ws -StartColumn 3 -BorderAround Thin -Italic -Underline -FontSize 14 -Value {"=sum($columnName`2:$columnName$endrow)" } -VerticalAlignment Bottom + Set-ExcelRange -Address $excel.Workbook.Worksheets["Sheet1"].cells["b3"] -HorizontalAlignment Right -VerticalAlignment Center -BorderAround Thick -BorderColor Red -StrikeThru + Set-ExcelRange -Address $excel.Workbook.Worksheets["Sheet1"].cells["c3"] -BorderColor Red -BorderTop DashDot -BorderLeft DashDotDot -BorderBottom Dashed -BorderRight Dotted + Set-ExcelRange -WorkSheet $ws -Range "E3" -Bold:$false -FontShift Superscript -HorizontalAlignment Left + Set-ExcelRange -WorkSheet $ws -Range "E1" -ResetFont -HorizontalAlignment General + Set-ExcelRange -Address $ws.cells["E7"] -ResetFont -WrapText -BackgroundColor AliceBlue -BackgroundPattern DarkTrellis -PatternColor Red -NumberFormat "£#,###.00" + Set-ExcelRange -Address $ws.Column(1) -Width 0 + Set-ExcelRange -Address $ws.Column(2) -AutoFit + Set-ExcelRange -Address $ws.Cells["E:E"] -AutoFit + Set-ExcelRange -Address $ws.row(5) -Height 0 $rr = $r.row - Set-Format -WorkSheet $ws -Range "B$rr" -Value "Total" + Set-ExcelRange -WorkSheet $ws -Range "B$rr" -Value "Total" $BadHideWarnvar = $null - Set-Format -WorkSheet $ws -Range "D$rr" -Formula "=E$rr/C$rr" -Hidden -WarningVariable "BadHideWarnvar" -WarningAction SilentlyContinue + Set-ExcelRange -WorkSheet $ws -Range "D$rr" -Formula "=E$rr/C$rr" -Hidden -WarningVariable "BadHideWarnvar" -WarningAction SilentlyContinue $rr ++ - Set-Format -WorkSheet $ws -Range "B$rr" -Value ([datetime]::Now) + Set-ExcelRange -WorkSheet $ws -Range "B$rr" -Value ([datetime]::Now) Close-ExcelPackage $excel -Calculate $excel = Open-ExcelPackage $path $ws = $excel.Workbook.Worksheets["Sheet1"] } - Context "Set-Row and Set-Column" { + Context "Set-ExcelRow and Set-ExcelColumn" { it "Set a row and a column to have zero width/height " { $r | Should not beNullorEmpty # $c | Should not beNullorEmpty ## can't see why but this test breaks in appveyor @@ -214,7 +213,7 @@ Describe "Set-Column, Set-Row and Set Format" { } } - Context "Set-Format value setting " { + Context "Set-ExcelRange value setting " { it "Inserted a formula " { $ws.Cells["D7"].Formula | Should be "E7/C7" } @@ -226,15 +225,15 @@ Describe "Set-Column, Set-Row and Set Format" { } } - Context "Set-Column Value Setting" { + Context "Set-ExcelColumn Value Setting" { BeforeAll { Remove-Item -Path $path -ErrorAction SilentlyContinue $excel = $DriverData | Export-Excel -PassThru -Path $path -AutoSize -AutoNameRange $ws = $excel.Workbook.Worksheets[1] - Set-Column -Worksheet $ws -Heading "Link" -AutoSize -Value {"https://en.wikipedia.org" + $worksheet.cells["B$Row"].value } - $c = Set-Column -PassThru -Worksheet $ws -Heading "NextBirthday" -Value { + Set-ExcelColumn -Worksheet $ws -Heading "Link" -AutoSize -Value {"https://en.wikipedia.org" + $worksheet.cells["B$Row"].value } + $c = Set-ExcelColumn -PassThru -Worksheet $ws -Heading "NextBirthday" -Value { $bmonth = $worksheet.cells["C$Row"].value.month ; $bDay = $worksheet.cells["C$Row"].value.day $cMonth = [datetime]::Now.Month ; $cday = [datetime]::Now.day ; $cyear = [datetime]::Now.Year if (($cmonth -gt $bmonth) -or (($cMonth -eq $bmonth) -and ($cday -ge $bDay))){ @@ -242,8 +241,8 @@ Describe "Set-Column, Set-Row and Set Format" { } else {[datetime]::new($cyear, $bmonth, $bday) } } - Set-Column -Worksheet $ws -Heading "Age" -Value "=INT((NOW()-DateOfBirth)/365)" - Set-Format -Address $c,$ws.column(3) -NumberFormat 'Short Date' -AutoSize + Set-ExcelColumn -Worksheet $ws -Heading "Age" -Value "=INT((NOW()-DateOfBirth)/365)" + Set-ExcelRange -Address $c,$ws.column(3) -NumberFormat 'Short Date' -AutoSize Close-ExcelPackage -ExcelPackage $excel -Calculate $excel = Open-ExcelPackage $path @@ -268,20 +267,77 @@ Describe "Set-Column, Set-Row and Set Format" { Describe "Conditional Formatting" { BeforeAll { - Remove-Item $path - $data = Get-Process | where company | select company,name,pm,handles,*mem* + Remove-Item $path + $data = Get-Process | Where-Object company | Select-Object company,name,pm,handles,*mem* $cfmt = New-ConditionalFormattingIconSet -Range "c:c" -ConditionalFormat ThreeIconSet -IconType Arrows $data | Export-Excel -path $Path -AutoSize -ConditionalFormat $cfmt - $excel = Open-ExcelPackage -Path $path + $excel = Open-ExcelPackage -Path $path $ws = $excel.Workbook.Worksheets[1] } Context "Using a pre-prepared 3 Arrows rule" { it "Set the right type, IconSet and range " { - $ws.ConditionalFormatting[0].IconSet | Should be "Arrows" - $ws.ConditionalFormatting[0].Address.Address | Should be "c:c" + $ws.ConditionalFormatting[0].IconSet | Should be "Arrows" + $ws.ConditionalFormatting[0].Address.Address | Should be "c:c" $ws.ConditionalFormatting[0].Type.ToString() | Should be "ThreeIconSet" - } + } } } +$path = "$Env:TEMP\test.xlsx" +$data2 = ConvertFrom-Csv -InputObject @" +ID,Product,Quantity,Price,Total +12001,Nails,37,3.99,147.63 +12002,Hammer,5,12.10,60.5 +12003,Saw,12,15.37,184.44 +12010,Drill,20,8,160 +12011,Crowbar,7,23.48,164.36 +12001,Nails,53,3.99,211.47 +12002,Hammer,6,12.10,72.60 +12003,Saw,10,15.37,153.70 +12010,Drill,10,8,80 +12012,Pliers,2,14.99,29.98 +12001,Nails,20,3.99,79.80 +12002,Hammer,2,12.10,24.20 +12010,Drill,11,8,88 +12012,Pliers,3,14.99,44.97 +"@ + +Describe "Table Formatting" { + BeforeAll { + Remove-Item $path + $excel = $data2 | Export-excel -path $path -WorksheetName Hardware -AutoNameRange -AutoSize -BoldTopRow -FreezeTopRow -PassThru + $ws = $excel.Workbook.Worksheets[1] + #test showfilter & TotalSettings + $Table = Add-ExcelTable -PassThru -Range $ws.cells[$($ws.Dimension.address)] -TableStyle Light1 -TableName HardwareTable -TotalSettings @{"Total"="Sum"} -ShowFirstColumn -ShowFilter:$false + #test expnading named number formats + Set-ExcelColumn -Worksheet $ws -Column 4 -NumberFormat 'Currency' + Set-ExcelColumn -Worksheet $ws -Column 5 -NumberFormat 'Currency' + $PtDef =New-PivotTableDefinition -PivotTableName Totals -PivotRows Product -PivotData @{"Total"="Sum"} -PivotNumberFormat Currency -PivotTotals None -PivotTableSyle Dark2 + Export-excel -ExcelPackage $excel -WorksheetName Hardware -PivotTableDefinition $PtDef + $excel= Open-ExcelPackage -Path $path + $ws1 = $excel.Workbook.Worksheets["Hardware"] + $ws2 = $excel.Workbook.Worksheets["Totals"] + } + Context "Setting and not clearing when Export-Excel touches the file again."{ + it "Set the Table Options " { + $ws1.Tables[0].Address.Address | should be "A1:E16" + $ws1.Tables[0].Name | should be "HardwareTable" + $ws1.Tables[0].ShowFirstColumn | should be $true + $ws1.Tables[0].ShowLastColumn | should not be $true + $ws1.Tables[0].ShowTotal | should be $true + $ws1.Tables[0].Columns["Total"].TotalsRowFunction | Should be "Sum" + $ws1.Tables[0].StyleName | should be "TableStyleLight1" + $ws1.Cells["D4"].Style.Numberformat.Format | Should match ([regex]::Escape([cultureinfo]::CurrentCulture.NumberFormat.CurrencySymbol)) + $ws1.Cells["E5"].Style.Numberformat.Format | Should match ([regex]::Escape([cultureinfo]::CurrentCulture.NumberFormat.CurrencySymbol)) + } + it "Set the Pivot Options " { + $ws2.PivotTables[0].DataFields[0].Format | Should match ([regex]::Escape([cultureinfo]::CurrentCulture.NumberFormat.CurrencySymbol)) + $ws2.PivotTables[0].ColumGrandTotals | Should be $false + $ws2.PivotTables[0].StyleName | Should be "PivotStyleDark2" + } + } +} + + +