mirror of
https://github.com/dfinke/ImportExcel.git
synced 2025-12-06 00:23:20 +00:00
275 lines
19 KiB
PowerShell
275 lines
19 KiB
PowerShell
try {
|
|
#ensure that color and font lookups are available
|
|
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
|
|
}
|
|
catch {}
|
|
Function Add-ConditionalFormatting {
|
|
<#
|
|
.Synopsis
|
|
Adds conditional formatting to all or part of a 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 two or three color scale where the color indicates the relative value
|
|
* Change the color, font, or number format of cells which meet given criteria
|
|
Add-ConditionalFormatting allows these parameters to be set; for fine tuning of
|
|
the rules, the -PassThru switch will return the rule so that you can modify
|
|
things which are specific to that type of rule, 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
|
|
Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "b2:b1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "2003"
|
|
Add-ConditionalFormatting -WorkSheet $excel.Workbook.Worksheets[1] -Address "i2:i1048576" -ForeGroundColor "RED" -RuleType ContainsText -ConditionValue "Disabled"
|
|
$excel.Workbook.Worksheets[1].Cells["D1:G1048576"].Style.Numberformat.Format = [cultureinfo]::CurrentCulture.DateTimeFormat.ShortDatePattern
|
|
$excel.Workbook.Worksheets[1].Row(1).style.font.bold = $true
|
|
$excel.Save() ; $excel.Dispose()
|
|
|
|
Here Export-Excel is called with the -PassThru parameter so the ExcelPackage object
|
|
representing Machines.XLSX is stored in $Excel.The desired worksheet is selected
|
|
and then columns" B" and "I" are conditionally formatted (excluding the top row)
|
|
to show red text if they contain "2003" or "Disabled" respectively.
|
|
A fixed date format is then applied to columns D to G, and the top row is formatted.
|
|
Finally the workbook is saved and the Excel package object is closed.
|
|
.Example
|
|
>
|
|
$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. 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 modified to apply the flags in reverse order, and transitions
|
|
between flags are set to 100 and 1000.
|
|
.Example
|
|
Add-ConditionalFormatting -WorkSheet $sheet -Range "D2:D1048576" -DataBarColor Red
|
|
|
|
This time $sheet holds an ExcelWorkshseet object and databars are added to
|
|
column D, excluding the top 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 condition
|
|
should apply, and instead of specifying a sheet and range within the sheet as
|
|
separate parameters, the cells where the format should apply are specified
|
|
directly. If a cell in the "FinishPosition" range is 1, then the text is
|
|
turned to Bold & Purple. This rule is moved to first in the priority list,
|
|
and where cells have a value of 1, no other rules will be processed.
|
|
.Example
|
|
>
|
|
$excel = Get-ChildItem | Select-Object -Property Name,Length,LastWriteTime,CreationTime | Export-Excel "$env:temp\test43.xlsx" -PassThru -AutoSize
|
|
$ws = $excel.Workbook.Worksheets["Sheet1"]
|
|
$ws.Cells["E1"].Value = "SavedAt"
|
|
$ws.Cells["F1"].Value = [datetime]::Now
|
|
$ws.Cells["F1"].Style.Numberformat.Format = (Expand-NumberFormat -NumberFormat 'Date-Time')
|
|
$lastRow = $ws.Dimension.End.Row
|
|
|
|
Add-ConditionalFormatting -WorkSheet $ws -address "A2:A$Lastrow" -RuleType LessThan -ConditionValue "A" -ForeGroundColor Gray
|
|
Add-ConditionalFormatting -WorkSheet $ws -address "B2:B$Lastrow" -RuleType GreaterThan -ConditionValue 1000000 -NumberFormat '#,###,,.00"M"'
|
|
Add-ConditionalFormatting -WorkSheet $ws -address "C2:C$Lastrow" -RuleType GreaterThan -ConditionValue "=INT($F$1-7)" -ForeGroundColor Green -StopIfTrue
|
|
Add-ConditionalFormatting -WorkSheet $ws -address "D2:D$Lastrow" -RuleType Equal -ConditionValue "=C2" -ForeGroundColor Blue -StopIfTrue
|
|
|
|
Close-ExcelPackage -Show $excel
|
|
|
|
The first few lines of code export a list of file and directory names, sizes
|
|
and dates to a spreadsheet. It puts the date of the export in cell F1.
|
|
The first Conditional format changes the color of files and folders that begin
|
|
with a ".", "_" or anything else which sorts before "A".
|
|
The second Conditional format changes the Number format of numbers bigger than
|
|
1 million, for example 1,234,567,890 will dispay as "1,234.57M"
|
|
The third highlights datestamps of files less than a week old when the export
|
|
was run; the = is necessary in the condition value otherwise the rule will
|
|
look for the the text INT($F$1-7), and the cell address for the date is fixed
|
|
using the standard Excel $ notation.
|
|
The final Conditional format looks for files which have not changed since they
|
|
were created. Here the condition value is "=C2". The = sign means C2 is treated
|
|
as a formula, not literal text. Unlike the file age, we want the cell used to
|
|
change for each cell where the conditional format applies. The first cell in
|
|
the conditional format range is D2, which is compared against C2, then D3 is
|
|
compared against C3 and so on. A common mistake is to include the title row in
|
|
the range and accidentally apply conditional formatting to it, or to begin the
|
|
range at row 2 but use row 1 as the starting point for comparisons.
|
|
.Example
|
|
Add-ConditionalFormatting $ws.Cells["B:B"] GreaterThan 10000000 -Fore Red -Stop -Pri 1
|
|
|
|
This version shows the shortest syntax - the Address, Ruletype, and
|
|
Conditionvalue can be identified from their position, and ForegroundColor,
|
|
StopIfTrue and Priority can all be shortend.
|
|
|
|
#>
|
|
Param (
|
|
#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 ,
|
|
#A standard named-rule - Top / Bottom / Less than / Greater than / Contains etc.
|
|
[Parameter(Mandatory = $true, ParameterSetName = "NamedRule", Position = 1)]
|
|
[OfficeOpenXml.ConditionalFormatting.eExcelConditionalFormattingRuleType]$RuleType ,
|
|
#Text color for matching objects
|
|
[Parameter(ParameterSetName = "NamedRule")]
|
|
[Alias("ForegroundColour","FontColor")]
|
|
$ForegroundColor,
|
|
#Color for databar type charts
|
|
[Parameter(Mandatory = $true, ParameterSetName = "DataBar")]
|
|
[Alias("DataBarColour")]
|
|
$DataBarColor,
|
|
#One of the three-icon set types (e.g. Traffic Lights)
|
|
[Parameter(Mandatory = $true, ParameterSetName = "ThreeIconSet")]
|
|
[OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting3IconsSetType]$ThreeIconsSet,
|
|
#A four-icon set name
|
|
[Parameter(Mandatory = $true, ParameterSetName = "FourIconSet")]
|
|
[OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting4IconsSetType]$FourIconsSet,
|
|
#A five-icon set name
|
|
[Parameter(Mandatory = $true, ParameterSetName = "FiveIconSet")]
|
|
[OfficeOpenXml.ConditionalFormatting.eExcelconditionalFormatting5IconsSetType]$FiveIconsSet,
|
|
#Use the Icon-Set in reverse order, or reverse the orders of Two- & Three-Color Scales
|
|
[Parameter(ParameterSetName = "NamedRule")]
|
|
[Parameter(ParameterSetName = "ThreeIconSet")]
|
|
[Parameter(ParameterSetName = "FourIconSet")]
|
|
[Parameter(ParameterSetName = "FiveIconSet")]
|
|
[switch]$Reverse,
|
|
#A value for the condition (for example 2000 if the test is 'lessthan 2000'; Formulas should begin with "=" )
|
|
[Parameter(ParameterSetName = "NamedRule",Position = 2)]
|
|
$ConditionValue,
|
|
#A second value for the conditions like "Between X and Y"
|
|
[Parameter(ParameterSetName = "NamedRule",Position = 3)]
|
|
$ConditionValue2,
|
|
#Background color for matching items
|
|
[Parameter(ParameterSetName = "NamedRule")]
|
|
$BackgroundColor,
|
|
#Background pattern for matching items
|
|
[Parameter(ParameterSetName = "NamedRule")]
|
|
[OfficeOpenXml.Style.ExcelFillStyle]$BackgroundPattern = [OfficeOpenXml.Style.ExcelFillStyle]::None ,
|
|
#Secondary color when a background pattern requires it
|
|
[Parameter(ParameterSetName = "NamedRule")]
|
|
$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 processing
|
|
[int]$Priority,
|
|
#If specified pass the rule back to the caller to allow additional customization.
|
|
[switch]$PassThru
|
|
)
|
|
|
|
#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 = (New-Object 'OfficeOpenXml.ExcelAddress' @(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 Icon-Set rule in this way; please use -$RuleType <SetName>." ; return}
|
|
if ($PSBoundParameters.ContainsKey("DataBarColor" ) ) {if ($DataBarColor -is [string]) {$DataBarColor = [System.Drawing.Color]::$DataBarColor }
|
|
$rule = $WorkSheet.ConditionalFormatting.AddDatabar( $Address , $DataBarColor )
|
|
}
|
|
elseif ($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 )}
|
|
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."}
|
|
}
|
|
#endregion
|
|
#region set the rule conditions
|
|
#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 ($PSBoundParameters.ContainsKey("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 ($PSBoundParameters.ContainsKey("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) }
|
|
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 }
|
|
if ($PSBoundParameters.ContainsKey("ForeGroundColor" ) ) {if ($ForeGroundColor -is [string]) {$ForeGroundColor = [System.Drawing.Color]::$ForeGroundColor }
|
|
$rule.Style.Font.Color.color = $ForeGroundColor }
|
|
if ($PSBoundParameters.ContainsKey("BackgroundColor" ) ) {if ($BackgroundColor -is [string]) {$BackgroundColor = [System.Drawing.Color]::$BackgroundColor }
|
|
$rule.Style.Fill.BackgroundColor.color = $BackgroundColor }
|
|
if ($PSBoundParameters.ContainsKey("BackgroundPattern") ) {$rule.Style.Fill.PatternType = $BackgroundPattern }
|
|
if ($PSBoundParameters.ContainsKey("PatternColor" ) ) {if ($PatternColor -is [string]) {$PatternColor = [System.Drawing.Color]::$PatternColor }
|
|
$rule.Style.Fill.PatternColor.color = $PatternColor }
|
|
#endregion
|
|
#Allow further tweaking by returning the rule, if passthru specified
|
|
if ($Passthru) {$rule}
|
|
} |