Compare commits

...

78 Commits

Author SHA1 Message Date
dfinke
91b0e8b0ed update https://github.com/dfinke/ImportExcel/pull/1145 2022-04-19 09:26:20 -04:00
dfinke
b997e90e78 bump version 2022-04-19 09:23:36 -04:00
Doug Finke
4d8710d017 Merge pull request #1145 from jamesmmueller/master
Allow for escaping of single quote in data.
2022-04-19 09:12:20 -04:00
Doug Finke
0929a442a5 Merge pull request #1149 from caosborn/CO_Examples-AddWorksheetVBA
Adding VBA examples for worksheets
2022-03-27 10:21:15 -04:00
Doug Finke
895a5c0cb2 Merge pull request #1150 from dfinke/isolate-build-issues
Fixes `Get-Service` fail on Windows 2022
2022-03-27 09:44:52 -04:00
dfinke
d7901af8f3 try always importing the clixml 2022-03-27 09:37:15 -04:00
dfinke
34f55a3659 suspect Get-Service 2022-03-25 19:32:53 -04:00
dfinke
606988bcf6 rigger build 2022-03-25 18:45:06 -04:00
Craig Osborn
441f2ada22 fix: Typo in filename 2022-03-25 08:43:48 +00:00
Craig Osborn
29ea7012d7 examples: Add VBA module and call in all worksheets 2022-03-25 08:36:02 +00:00
Craig Osborn
67ac63ddcf examples: Add VBA to single worksheet 2022-03-25 08:35:13 +00:00
James Mueller
c939c6ecb0 Merge pull request #1 from jamesmmueller/jamesmmueller-patch-1
ConvertFrom-ExcelToSQLInsert fix single quotes
2022-03-17 10:55:37 -05:00
James Mueller
96493e059b ConvertFrom-ExcelToSQLInsert fix single quotes
If data in the Excel file contains single quotes, then the insert will bomb as it will be malformed. Quotes would need escaped. Not all languages will support the same escape method, so I recommend it being set by an optional parameter.

Example of what will fail:
  Name
  -----------
  Fry's

INSERT INTO [Name] Values ( 'Fry's' ) 

Corrected for quotes (such as needing to escape a single quote with an additional single quote)

INSERT INTO [Name] Values ('Fry''s') 


Example code:
$ConvertToSqlParams = @{TableName = ‘dbo.Names’
Path = ‘C:\temp\data.xlsx’
ConvertEmptyStringsToNull = $true
UseMSSQLSyntax = $true
SingleQuoteStyle = "''"
}

$SQLInsert = ConvertFrom-ExcelToSqlInsert @ConvertToSqlParams
2022-03-11 16:09:38 -06:00
Doug Finke
cb7f2a06f4 Remove vscode badge 2022-03-06 14:52:37 -05:00
Doug Finke
99beda7a10 Move to mixed case folders 2022-03-06 14:51:30 -05:00
dfinke
0c8bb2300a update links 2022-02-04 17:31:39 -05:00
dfinke
c09083d350 updated 2022-02-04 17:30:40 -05:00
dfinke
556f0ac51e Updated changelog 2022-02-04 17:28:30 -05:00
Doug Finke
1e4fc59a25 Merge pull request #1133 from joshooaj/master
Add Add-ExcelImage example
2022-02-04 15:48:05 -05:00
dfinke
5ab6a9116d @joshooaj added throw if not on Windows, pls review 2022-02-03 18:43:15 -05:00
Josh Hendricks
f33215382a docs: rename to AddImage for consistency 2022-02-02 08:31:35 -08:00
Josh Hendricks
cf5d3f83d6 docs: Update readme 2022-02-02 08:16:22 -08:00
Josh Hendricks
91da711635 docs: add Add-ExcelImage example 2022-02-01 21:37:55 -08:00
Doug Finke
eec5e343d4 Merge pull request #1118 from dfinke/add-freeze-pane-example
Add FreezePane Example
2021-12-30 14:46:52 -05:00
dfinke
d69b640edc Add FreezePane Example 2021-12-30 14:39:33 -05:00
Doug Finke
2ca870a889 Merge pull request #1112 from dfinke/refactor-rezip 2021-11-30 18:44:24 -05:00
Doug Finke
a320cfd28c Merge pull request #1110 from DavisHenckel/FAQ_Docs 2021-11-30 18:32:59 -05:00
dfinke
aa1b042767 Add -ReZip to Close-ExcelPackage like in Export-Excel #1111 2021-11-30 15:50:11 -05:00
dfinke
7e8416d67c Bump version and update change log 2021-11-30 15:49:07 -05:00
Davis Henckel
229b60b25d Remove Unnecessary Import & Add Header
Removed unnecessary import-module statement. Added a header to match the styling of other documents.
2021-11-28 20:03:07 -08:00
Davis Henckel
5700989321 Explain functionality of Open/Close-ExcelPackage 2021-11-28 11:36:37 -08:00
Davis Henckel
56e1704e7e Added missing " || improved readability. 2021-11-28 11:25:21 -08:00
Davis Henckel
8268bbc2e1 Merge pull request #1 from DavisHenckel/FAQ_Docs_2
Faq docs 2
2021-11-26 14:56:17 -08:00
Davis Henckel
5657659331 Add additional dbg info, Doc complete.
Changed variable name in example code, added additional info to Data structure explanation. Final proofread for punctuation and style. Document complete.
2021-11-26 14:53:21 -08:00
Davis Henckel
0d4a32e266 Change phrasing to show better MD style
Added additional headings to read more easily.
2021-11-26 14:42:44 -08:00
Davis Henckel
1c8f8d2a3d Update image to include row/col data num 2021-11-26 14:41:37 -08:00
Davis Henckel
f6a65677df Merge branch 'FAQ_Docs_2' of https://github.com/DavisHenckel/ImportExcel into FAQ_Docs_2 2021-11-26 14:34:23 -08:00
Davis Henckel
86a7865fb2 Add multiple images. 2021-11-26 14:34:17 -08:00
Davis Henckel
8b3bf4f14f Added Debug image showing var contents 2021-11-26 14:33:48 -08:00
Davis Henckel
4d6193f549 Complete Majority of Write to ExistingExcelFile md 2021-11-26 14:28:04 -08:00
Davis Henckel
6ebac7b6dc Add newlines 2021-11-26 12:18:05 -08:00
Davis Henckel
d71dd36d56 First version of file, with Image. 2021-11-26 12:17:39 -08:00
Davis Henckel
3697cdfeee Merge branch 'FAQ_Docs_2' of https://github.com/DavisHenckel/ImportExcel into FAQ_Docs_2 2021-11-26 12:17:06 -08:00
Davis Henckel
1e172cf21f Add Excel Pkg Data Img 2021-11-26 12:16:41 -08:00
Davis Henckel
6da7553c98 Merge branch 'FAQ_Docs' of https://github.com/DavisHenckel/ImportExcel into FAQ_Docs_2 2021-11-26 10:55:43 -08:00
Davis Henckel
5a444c620b Punctuation change 2021-11-26 10:25:30 -08:00
Davis Henckel
4a09fc3570 Punctuation change 2021-11-26 10:25:14 -08:00
Davis Henckel
d706a10276 Complete Document
Demonstrates how to create a blank excel file.
2021-11-26 10:23:09 -08:00
Davis Henckel
1fd2f422cd Create 2 more MD files to create 2021-11-26 10:15:01 -08:00
Davis Henckel
283e50547d Styling Consistency & Punctuation 2021-11-26 09:11:56 -08:00
Davis Henckel
15211a6297 Update Wording.
Punctuation and wording changes.
2021-11-26 09:08:31 -08:00
Davis Henckel
8905b8d401 Fix small typo
Comment in first image should have read "#Loads the Excel file into a PSCustomObject"
2021-11-26 08:58:44 -08:00
Davis Henckel
e9b437af4e Complete first version of ExistingExcelFile FAQ
Added loading a row/col, then mapping to HashTable
2021-11-26 06:56:08 -08:00
Davis Henckel
330e237727 Update Image Paths so they are Relative 2021-11-26 06:31:53 -08:00
Davis Henckel
c56b2cd33a Add first 2 images
Images display debugger and contents of excel file.
2021-11-21 16:56:52 -08:00
Davis Henckel
1aa5c6da45 add initial 2 images for first md FAQ doc 2021-11-21 16:51:14 -08:00
Davis Henckel
9eb894cf59 create directory, add initial md file w/first doc 2021-11-21 16:50:34 -08:00
dfinke
3a4c2d7bd9 bump version 2021-11-20 16:18:12 -05:00
dfinke
42cb5a316a update changelog 2021-11-20 16:18:05 -05:00
dfinke
536cdaa841 tweaked spelling 2021-11-20 15:53:23 -05:00
Doug Finke
6cd9fad7ba Merge pull request #1106 from royashbrook/CI-Updates
Add ACE into testing pipeline
2021-11-20 15:44:13 -05:00
Doug Finke
829d854c3d Merge pull request #1102 from muschebubusche/AddSelectiveColumnImport
Add selective column import
2021-11-20 15:36:05 -05:00
muschebubusche
b33a282740 Add new pester test 2021-11-20 16:51:49 +01:00
muschebubusche
73fc96166c Refactor selective column import
Move code into the function Get-PropertyNames and remove the rest.
Now it only replaces $Columns with $ImportColumns after a couple of checks. So the heavy work like arranging and getting the right values is done in the original way of Import-Excel.
2021-11-20 16:51:40 +01:00
Roy Ashbrook
00f7278115 add back *nix tests 2021-11-16 16:28:03 -05:00
Roy Ashbrook
956cf5aa49 fix 2021-11-16 16:23:54 -05:00
Roy Ashbrook
877310e015 add back pscore 2021-11-16 16:20:59 -05:00
Roy Ashbrook
08bf877535 add back in normal win testing 2021-11-16 16:11:32 -05:00
Roy Ashbrook
88e28a1d6c change path 2021-11-16 16:05:33 -05:00
Roy Ashbrook
eb63fe259a testing caching 2021-11-16 16:02:19 -05:00
Roy Ashbrook
6d97efc5c2 fun with curl... maybe 2021-11-16 15:43:57 -05:00
Roy Ashbrook
c567526eac testing caching 2021-11-16 15:34:36 -05:00
Roy Ashbrook
ed210cc730 testing 2021-11-16 15:29:32 -05:00
Roy Ashbrook
72f44ebcb9 trying a folder 2021-11-16 15:18:37 -05:00
Roy Ashbrook
f1d20ed163 test pipeline cache 2021-11-16 15:13:52 -05:00
Roy Ashbrook
02cf6bb3f3 test ace 2021-11-16 09:03:53 -05:00
muschebubusche
8f0fc7397d Add example and automatic column arrangement
Add example and automatic column arrangement
2021-11-09 22:40:40 +01:00
muschebubusche
62c8d74a59 Add selective column import 2021-11-03 23:15:18 +01:00
72 changed files with 1058 additions and 385 deletions

View File

@@ -0,0 +1,119 @@
function Add-ExcelImage {
<#
.SYNOPSIS
Adds an image to a worksheet in an Excel package.
.DESCRIPTION
Adds an image to a worksheet in an Excel package using the
`WorkSheet.Drawings.AddPicture(name, image)` method, and places the
image at the location specified by the Row and Column parameters.
Additional position adjustment can be made by providing RowOffset and
ColumnOffset values in pixels.
.EXAMPLE
$image = [System.Drawing.Image]::FromFile($octocat)
$xlpkg = $data | Export-Excel -Path $path -PassThru
$xlpkg.Sheet1 | Add-ExcelImage -Image $image -Row 4 -Column 6 -ResizeCell
Where $octocat is a path to an image file, and $data is a collection of
data to be exported, and $path is the output path for the Excel document,
Add-Excel places the image at row 4 and column 6, resizing the column
and row as needed to fit the image.
.INPUTS
[OfficeOpenXml.ExcelWorksheet]
.OUTPUTS
None
#>
[CmdletBinding()]
param(
# Specifies the worksheet to add the image to.
[Parameter(Mandatory, ValueFromPipeline)]
[OfficeOpenXml.ExcelWorksheet]
$WorkSheet,
# Specifies the Image to be added to the worksheet.
[Parameter(Mandatory)]
[System.Drawing.Image]
$Image,
# Specifies the row where the image will be placed. Rows are counted from 1.
[Parameter(Mandatory)]
[ValidateRange(1, [int]::MaxValue)]
[int]
$Row,
# Specifies the column where the image will be placed. Columns are counted from 1.
[Parameter(Mandatory)]
[ValidateRange(1, [int]::MaxValue)]
[int]
$Column,
# Specifies the name to associate with the image. Names must be unique per sheet.
# Omit the name and a GUID will be used instead.
[Parameter()]
[string]
$Name,
# Specifies the number of pixels to offset the image on the Y-axis. A
# positive number moves the image down by the specified number of pixels
# from the top border of the cell.
[Parameter()]
[int]
$RowOffset = 1,
# Specifies the number of pixels to offset the image on the X-axis. A
# positive number moves the image to the right by the specified number
# of pixels from the left border of the cell.
[Parameter()]
[int]
$ColumnOffset = 1,
# Increase the column width and row height to fit the image if the current
# dimensions are smaller than the image provided.
[Parameter()]
[switch]
$ResizeCell
)
begin {
if ($IsWindows -eq $false) {
throw "This only works on Windows and won't run on $([environment]::OSVersion)"
}
<#
These ratios work on my machine but it feels fragile. Need to better
understand how row and column sizing works in Excel and what the
width and height units represent.
#>
$widthFactor = 1 / 7
$heightFactor = 3 / 4
}
process {
if ([string]::IsNullOrWhiteSpace($Name)) {
$Name = (New-Guid).ToString()
}
if ($null -ne $WorkSheet.Drawings[$Name]) {
Write-Error "A picture with the name `"$Name`" already exists in worksheet $($WorkSheet.Name)."
return
}
<#
The row and column offsets of 1 ensures that the image lands just
inside the gray cell borders at the top left.
#>
$picture = $WorkSheet.Drawings.AddPicture($Name, $Image)
$picture.SetPosition($Row - 1, $RowOffset, $Column - 1, $ColumnOffset)
if ($ResizeCell) {
<#
Adding 1 to the image height and width ensures that when the
row and column are resized, the bottom right of the image lands
just inside the gray cell borders at the bottom right.
#>
$width = $widthFactor * ($Image.Width + 1)
$height = $heightFactor * ($Image.Height + 1)
$WorkSheet.Column($Column).Width = [Math]::Max($width, $WorkSheet.Column($Column).Width)
$WorkSheet.Row($Row).Height = [Math]::Max($height, $WorkSheet.Row($Row).Height)
}
}
}

View File

@@ -0,0 +1,39 @@
if ($IsWindows -eq $false) {
throw "This only works on Windows and won't run on $([environment]::OSVersion)"
}
Add-Type -AssemblyName System.Drawing
. $PSScriptRoot\Add-ExcelImage.ps1
$data = ConvertFrom-Csv @"
Region,State,Units,Price
West,Texas,927,923.71
North,Tennessee,466,770.67
East,Florida,520,458.68
East,Maine,828,661.24
West,Virginia,465,053.58
North,Missouri,436,235.67
South,Kansas,214,992.47
North,North Dakota,789,640.72
South,Delaware,712,508.55
"@
$path = "$PSScriptRoot/Add-Picture-test.xlsx"
Remove-Item $path -ErrorAction SilentlyContinue
try {
$octocat = "$PSScriptRoot/Octocat.jpg"
$image = [System.Drawing.Image]::FromFile($octocat)
$xlpkg = $data | Export-Excel -Path $path -PassThru
$xlpkg.Sheet1 | Add-ExcelImage -Image $image -Row 4 -Column 6 -ResizeCell
}
finally {
if ($image) {
$image.Dispose()
}
if ($xlpkg) {
Close-ExcelPackage -ExcelPackage $xlpkg -Show
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -0,0 +1,33 @@
# Add-ExcelImage Example
Adding pictures to an Excel worksheet is possible by calling the `AddPicture(name, image)`
method on the `Drawings` property of an `ExcelWorksheet` object.
The `Add-ExcelImage` example here demonstrates how to add a picture at a given
cell location, and optionally resize the row and column to fit the image.
## Running the example
To try this example, run the script `AddImage.ps1`. The `Add-ExcelImage`
function will be dot-sourced, and an Excel document will be created in the same
folder with a sample data set. The Octocat image will then be embedded into
Sheet1.
The creation of the Excel document and the `System.Drawing.Image` object
representing Octocat are properly disposed within a `finally` block to ensure
that the resources are released, even if an error occurs in the `try` block.
## Note about column and row sizing
Care has been taken in this example to get the image placement to be just inside
the cell border, and if the `-ResizeCell` switch is present, the height and width
of the row and column will be increased, if needed, so that the bottom right of
the image also lands just inside the cell border.
The Excel row and column sizes are measured in "point" units rather than pixels,
and a fixed multiplication factor is used to convert the size of the image in
pixels, to the corresponding height and width values in Excel.
It's possible that different DPI or text scaling options could result in
imperfect column and row sizing and if a better strategy is found for converting
the image dimensions to column and row sizes, this example will be updated.

View File

@@ -0,0 +1,54 @@
# Freeze the columns/rows to left and above the cell
$data = ConvertFrom-Csv @"
Region,State,Units,Price,Name,NA,EU,JP,Other
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
West,Texas,927,923.71,Wii Sports,41.49,29.02,3.77,8.46
"@
$xlfilename = "test.xlsx"
Remove-Item $xlfilename -ErrorAction SilentlyContinue
<#
Freezes the top two rows and the two leftmost column
#>
$data | Export-Excel $xlfilename -Show -Title 'Sales Data' -FreezePane 3, 3

View File

@@ -0,0 +1,9 @@
try {Import-Module $PSScriptRoot\..\..\ImportExcel.psd1} catch {throw ; return}
# Create example file
$xlFile = "$PSScriptRoot\ImportColumns.xlsx"
Get-Process | Export-Excel -Path $xlFile
# -ImportColumns will also arrange columns
Import-Excel -Path $xlFile -ImportColumns @(1,3,2) -NoHeader -StartRow 1
# Get only pm, npm, cpu, id, processname
Import-Excel -Path $xlFile -ImportColumns @(6,7,12,25,46) | Format-Table -AutoSize

View File

@@ -0,0 +1,64 @@
$xlfile = "$env:temp\test.xlsm"
Remove-Item $xlfile -ErrorAction SilentlyContinue
ConvertFrom-Csv @"
Region,Item,TotalSold
West,screwdriver,98
West,kiwi,19
North,kiwi,47
West,screws,48
West,avocado,52
East,avocado,40
South,drill,61
North,orange,92
South,drill,29
South,saw,36
"@ | Export-Excel $xlfile -TableName 'Sales' -WorksheetName 'Sales' -AutoSize
$Excel = ConvertFrom-Csv @"
Supplier,Item,TotalBought
Hardware,screwdriver,98
Groceries,kiwi,19
Hardware,screws,48
Groceries,avocado,52
Hardware,drill,61
Groceries,orange,92
Hardware,drill,29
HArdware,saw,36
"@ | Export-Excel $xlfile -TableName 'Purchases' -WorksheetName 'Purchases' -PassThru -AutoSize
$wb = $Excel.Workbook
$wb.CreateVBAProject()
# Create a module with a sub to highlight the selected row & column of the active table.
# https://docs.microsoft.com/en-gb/office/vba/excel/Concepts/Cells-and-Ranges/highlight-the-active-cell-row-or-column
$codeModule = @"
Public Sub HighlightSelection(ByVal Target As Range)
' Clear the color of all the cells
Cells.Interior.ColorIndex = 0
If Target.Cells.Count > 1 Then Exit Sub
Application.ScreenUpdating = False
With ActiveCell
' Highlight the row and column that contain the active cell, within the current region
Range(Cells(.Row, .CurrentRegion.Column), Cells(.Row, .CurrentRegion.Columns.Count + .CurrentRegion.Column - 1)).Interior.ColorIndex = 38
Range(Cells(.CurrentRegion.Row, .Column), Cells(.CurrentRegion.Rows.Count + .CurrentRegion.Row - 1, .Column)).Interior.ColorIndex = 24
End With
Application.ScreenUpdating = True
End Sub
"@
$module = $wb.VbaProject.Modules.AddModule("PSExcelModule")
$module.Code = $codeModule
# Add a call to the row & column highlight sub on each worksheet.
$codeSheet = @"
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
HighlightSelection Target
End Sub
"@
foreach ($sheet in $wb.Worksheets) {
$sheet.CodeModule.Code = $codeSheet
}
Close-ExcelPackage $Excel -Show

View File

@@ -0,0 +1,41 @@
$xlfile = "$env:temp\test.xlsm"
Remove-Item $xlfile -ErrorAction SilentlyContinue
$Excel = ConvertFrom-Csv @"
Region,Item,TotalSold
West,screwdriver,98
West,kiwi,19
North,kiwi,47
West,screws,48
West,avocado,52
East,avocado,40
South,drill,61
North,orange,92
South,drill,29
South,saw,36
"@ | Export-Excel $xlfile -TableName 'Sales' -WorksheetName 'Sales' -AutoSize -PassThru
$wb = $Excel.Workbook
$sheet = $wb.Worksheets["Sales"]
$wb.CreateVBAProject()
# Add a sub to the 'Worksheet_SelectionChange' event of the worksheet to highlight the selected row & column of the active table.
# https://docs.microsoft.com/en-gb/office/vba/excel/Concepts/Cells-and-Ranges/highlight-the-active-cell-row-or-column
$code = @"
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' Clear the color of all the cells
Cells.Interior.ColorIndex = 0
If Target.Cells.Count > 1 Then Exit Sub
Application.ScreenUpdating = False
With ActiveCell
' Highlight the row and column that contain the active cell, within the current region
Range(Cells(.Row, .CurrentRegion.Column), Cells(.Row, .CurrentRegion.Columns.Count + .CurrentRegion.Column - 1)).Interior.ColorIndex = 38
Range(Cells(.CurrentRegion.Row, .Column), Cells(.CurrentRegion.Rows.Count + .CurrentRegion.Row - 1, .Column)).Interior.ColorIndex = 24
End With
Application.ScreenUpdating = True
End Sub
"@
$sheet.CodeModule.Code = $code
Close-ExcelPackage $Excel -Show

View File

@@ -0,0 +1,6 @@
# Create an Empty Excel File
Use an empty string to export to an excel file.
```powershell
#Build an Excel file named: "file.xlsx" containing a worksheet: "MyWorksheet"
"" | Export-Excel -Path "C:\Test\file.xlsx" -WorksheetName "MyWorksheet"
```

View File

@@ -0,0 +1,41 @@
# How to Read an Existing Excel File
## Enumerate the Excel File Contents
```powershell
#Load the Excel file into a PSCustomObject
$ExcelFile = Import-Excel "C:\Test\file.xlsx" -WorksheetName "Sheet1"
```
## Visual of Data Structure
The File C:\Test\file.xlsx contains
![ExcelFileContents](/images/FAQ_Images/ExcelFileContents.png)
After loading this data into ```$ExcelFile``` the data is stored like:
![ExcelFileDebugImg](/images/FAQ_Images/ExcelFileDebugImg.jpg)
## Other Common Operations
### Load a Column
```powershell
$SpecificColumn = $ExcelFile."anotherHeader" #loads column with the header "anotherHeader" -- data stored in an array
```
### Load a Row
```powershell
$SpecificRow = $ExcelFile[1] #Loads row at index 1. Index 1 is the first row instead of 0.
```
### Map Contents to Hashtable to Interpret Data
Sometimes mapping to a Hashtable is more convenient to have access to common Hashtable operations. Enumerate a Hashtable with the row's data by:
```powershell
$HashTable = @{}
$SpecificRow= $ExcelFile[2]
$SpecificRow.psobject.properties | ForEach-Object {
$HashTable[$_.Name] = $_.Value
}
```
To then iterate through the enumerated Hashtable:
```powershell
ForEach ($Key in ($HashTable.GetEnumerator()) | Where-Object {$_.Value -eq "x"}){ #Only grabs a key where the value is "x"
#values accessible with $Key.Name or $Key.Value
}
```

View File

@@ -0,0 +1,34 @@
# Write to an Existing Excel File
### Enumerate the Excel File
The cmdlets ```Open-ExcelPackage``` and ```Close-ExcelPackage``` allow for direct modification to Excel file contents.
```powershell
$ExcelPkg = Open-ExcelPackage -Path "C:\Test\file.xlsx"
```
Contents of file.xlsx:
![ExcelFileContents](/images/FAQ_Images/ExcelFileContents.png)
### Enumerate the Worksheet to View or Modify the Data
```powershell
$WorkSheet = $ExcelPkg.Workbook.Worksheets["sheet1"].Cells #open excel worksheet cells from worksheet "sheet1"
```
Visual of data structure:
![DataStructureExcelPkg](/images/FAQ_Images/DataStructureExcelPkg.png)
A1 contains "someHeader", A2 contains "data1" etc.
### Modify a Specific Value in a File
Values can be accessed by row, column. Similar to a 2D array.
```powershell
$WorkSheet[1,4].Value = "New Column Header" #Starts at index 1 not 0
```
Contents of file.xlsx after modifying:
![ExcelFileContentsPostAdd](/images/FAQ_Images/ExcelFileContentsPostAdd.png)
### Load Value at Specific Index
```powershell
$ValueAtIndex = $WorkSheet[2,1].Value #Loads the value at row 2, column A
```
```$ValueAtIndex``` now contains: ![ValueAtIndexData](/images/FAQ_Images/ValueAtIndexData.png)
### Save File After Modifying
The changes will not display in the Excel file until Close-ExcelPackage is called.
```powershell
Close-ExcelPackage $ExcelPkg #close and save changes made to the Excel file.
```
**Note**: If the file is currently in use, Close-ExcelPackage will return an error and will not save the information.

View File

@@ -6,7 +6,7 @@
RootModule = 'ImportExcel.psm1'
# Version number of this module.
ModuleVersion = '7.3.1'
ModuleVersion = '7.4.2'
# ID used to uniquely identify this module
GUID = '60dd4136-feff-401a-ba27-a84458c57ede'

View File

@@ -0,0 +1,25 @@
function Invoke-ExcelReZipFile {
<#
#>
param(
[Parameter(Mandatory)]
[OfficeOpenXml.ExcelPackage]$ExcelPackage
)
Write-Verbose -Message "Re-Zipping $($ExcelPackage.file) using .NET ZIP library"
try {
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+"
continue
}
try {
$TempZipPath = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName())
$null = [io.compression.zipfile]::ExtractToDirectory($ExcelPackage.File, $TempZipPath)
Remove-Item $ExcelPackage.File -Force
$null = [io.compression.zipfile]::CreateFromDirectory($TempZipPath, $ExcelPackage.File)
Remove-Item $TempZipPath -Recurse -Force
}
catch { throw "Error resizipping $path : $_" }
}

View File

@@ -1,33 +1,38 @@
function Close-ExcelPackage {
[CmdLetBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
param (
[parameter(Mandatory=$true, ValueFromPipeline=$true)]
[parameter(Mandatory = $true, ValueFromPipeline = $true)]
[OfficeOpenXml.ExcelPackage]$ExcelPackage,
[switch]$Show,
[Switch]$Show,
[Switch]$NoSave,
$SaveAs,
[ValidateNotNullOrEmpty()]
[String]$Password,
[switch]$Calculate
[Switch]$Calculate,
[Switch]$ReZip
)
if ( $NoSave) {$ExcelPackage.Dispose()}
if ( $NoSave) { $ExcelPackage.Dispose() }
else {
if ($Calculate) {
try { [OfficeOpenXml.CalculationExtension]::Calculate($ExcelPackage.Workbook) }
catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook."}
try { [OfficeOpenXml.CalculationExtension]::Calculate($ExcelPackage.Workbook) }
catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook." }
}
if ($SaveAs) {
$SaveAs = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($SaveAs)
if ($Password) {$ExcelPackage.SaveAs( $SaveAs, $Password ) }
else {$ExcelPackage.SaveAs( $SaveAs)}
if ($Password) { $ExcelPackage.SaveAs( $SaveAs, $Password ) }
else { $ExcelPackage.SaveAs( $SaveAs) }
}
else {
if ($Password) {$ExcelPackage.Save($Password) }
else {$ExcelPackage.Save() }
else {
if ($Password) { $ExcelPackage.Save($Password) }
else { $ExcelPackage.Save() }
$SaveAs = $ExcelPackage.File.FullName
}
if ($ReZip) {
Invoke-ExcelReZipFile -ExcelPackage $ExcelPackage
}
$ExcelPackage.Dispose()
if ($Show) {Start-Process -FilePath $SaveAs }
if ($Show) { Start-Process -FilePath $SaveAs }
}
}

View File

@@ -16,12 +16,15 @@ function ConvertFrom-ExcelToSQLInsert {
[switch]$NoHeader,
[switch]$DataOnly,
[switch]$ConvertEmptyStringsToNull,
[switch]$UseMsSqlSyntax
[switch]$UseMsSqlSyntax,
[Parameter(Mandatory = $false)]
$SingleQuoteStyle
)
$null = $PSBoundParameters.Remove('TableName')
$null = $PSBoundParameters.Remove('ConvertEmptyStringsToNull')
$null = $PSBoundParameters.Remove('UseMsSqlSyntax')
$null = $PSBoundParameters.Remove('SingleQuoteStyle')
$params = @{} + $PSBoundParameters
@@ -38,11 +41,16 @@ function ConvertFrom-ExcelToSQLInsert {
'NULL'
}
else {
"'" + $record.$propertyName + "'"
if ( $SingleQuoteStyle ) {
"'" + $record.$propertyName.ToString().Replace("'",${SingleQuoteStyle}) + "'"
}
else {
"'" + $record.$propertyName + "'"
}
}
}
$targetValues = ($values -join ", ")
"INSERT INTO {0} ({1}) Values({2});" -f $TableName, $ColumnNames, $targetValues
}
}
}

View File

@@ -22,7 +22,7 @@
[Switch]$TitleBold,
[Int]$TitleSize = 22,
$TitleBackgroundColor,
[parameter(DontShow=$true)]
[parameter(DontShow = $true)]
[Switch]$IncludePivotTable,
[String]$PivotTableName,
[String[]]$PivotRows,
@@ -48,14 +48,14 @@
[Switch]$BoldTopRow,
[Switch]$NoHeader,
[ValidateScript( {
if (-not $_) { throw 'RangeName is null or empty.' }
elseif ($_[0] -notmatch '[a-z]') { throw 'RangeName starts with an invalid character.' }
if (-not $_) { throw 'RangeName is null or empty.' }
elseif ($_[0] -notmatch '[a-z]') { throw 'RangeName starts with an invalid character.' }
else { $true }
})]
})]
[String]$RangeName,
[Alias('Table')]
$TableName,
[OfficeOpenXml.Table.TableStyles]$TableStyle = [OfficeOpenXml.Table.TableStyles]::Medium6,
[OfficeOpenXml.Table.TableStyles]$TableStyle = [OfficeOpenXml.Table.TableStyles]::Medium6,
[Switch]$Barchart,
[Switch]$PieChart,
[Switch]$LineChart ,
@@ -88,7 +88,7 @@
[Switch]$Now,
[Switch]$ReturnRange,
#By default PivotTables 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")]
[ValidateSet("Both", "Columns", "Rows", "None")]
[String]$PivotTotals = "Both",
#Included for compatibility - equivalent to -PivotTotals "None"
[Switch]$NoTotalsInPivot,
@@ -98,13 +98,13 @@
begin {
$numberRegex = [Regex]'\d'
$isDataTypeValueType = $false
if ($NoClobber) {Write-Warning -Message "-NoClobber parameter is no longer used" }
if ($NoClobber) { Write-Warning -Message "-NoClobber parameter is no longer used" }
#Open the file, get the worksheet, and decide where in the sheet we are writing, and if there is a number format to apply.
try {
try {
$script:Header = $null
if ($Append -and $ClearSheet) {throw "You can't use -Append AND -ClearSheet." ; return}
if ($Append -and $ClearSheet) { throw "You can't use -Append AND -ClearSheet." ; return }
#To force -Now not to format as a table, allow $false in -TableName to be "No table"
$TableName = if ($null -eq $TableName -or ($TableName -is [bool] -and $false -eq $TableName)) { $null } else {[String]$TableName}
$TableName = if ($null -eq $TableName -or ($TableName -is [bool] -and $false -eq $TableName)) { $null } else { [String]$TableName }
if ($Now -or (-not $Path -and -not $ExcelPackage) ) {
if (-not $PSBoundParameters.ContainsKey("Path")) { $Path = [System.IO.Path]::GetTempFileName() -replace '\.tmp', '.xlsx' }
if (-not $PSBoundParameters.ContainsKey("Show")) { $Show = $true }
@@ -120,59 +120,59 @@
$pkg = $ExcelPackage
$Path = $pkg.File
}
Else { $pkg = Open-ExcelPackage -Path $Path -Create -KillExcel:$KillExcel -Password:$Password}
Else { $pkg = Open-ExcelPackage -Path $Path -Create -KillExcel:$KillExcel -Password:$Password }
}
catch {throw "Could not open Excel Package $path"}
try {
$params = @{WorksheetName=$WorksheetName}
foreach ($p in @("ClearSheet", "MoveToStart", "MoveToEnd", "MoveBefore", "MoveAfter", "Activate")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}}
catch { throw "Could not open Excel Package $path" }
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
if ($ws.Name -ne $WorksheetName) {
Write-Warning -Message "The Worksheet name has been changed from $WorksheetName to $($ws.Name), this may cause errors later."
$WorksheetName = $ws.Name
}
}
catch {throw "Could not get worksheet $WorksheetName"}
try {
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
$headerRange = $ws.Dimension.Address -replace "\d+$", $StartRow
#using a slightly odd syntax otherwise header ends up as a 2D array
$ws.Cells[$headerRange].Value | ForEach-Object -Begin {$Script:header = @()} -Process {$Script:header += $_ }
$ws.Cells[$headerRange].Value | ForEach-Object -Begin { $Script:header = @() } -Process { $Script:header += $_ }
$NoHeader = $true
#if we did not get AutoNameRange, but headers have ranges of the same name make autoNameRange True, otherwise make it false
if (-not $AutoNameRange) {
$AutoNameRange = $true ; foreach ($h in $header) {if ($ws.names.name -notcontains $h) {$AutoNameRange = $false} }
$AutoNameRange = $true ; foreach ($h in $header) { if ($ws.names.name -notcontains $h) { $AutoNameRange = $false } }
}
#if we did not get a Rangename but there is a Range which covers the active part of the sheet, set Rangename to that.
if (-not $RangeName -and $ws.names.where({$_.name[0] -match '[a-z]'})) {
if (-not $RangeName -and $ws.names.where({ $_.name[0] -match '[a-z]' })) {
$theRange = $ws.names.where({
($_.Name[0] -match '[a-z]' ) -and
($_.Start.Row -eq $StartRow) -and
($_.Start.Column -eq $StartColumn) -and
($_.End.Row -eq $ws.Dimension.End.Row) -and
($_.End.Column -eq $ws.Dimension.End.column) } , 'First', 1)
if ($theRange) {$rangename = $theRange.name}
($_.Name[0] -match '[a-z]' ) -and
($_.Start.Row -eq $StartRow) -and
($_.Start.Column -eq $StartColumn) -and
($_.End.Row -eq $ws.Dimension.End.Row) -and
($_.End.Column -eq $ws.Dimension.End.column) } , 'First', 1)
if ($theRange) { $rangename = $theRange.name }
}
#if we did not get a table name but there is a table which covers the active part of the sheet, set table name to that, and don't do anything with autofilter
$existingTable = $ws.Tables.Where({$_.address.address -eq $ws.dimension.address},'First', 1)
$existingTable = $ws.Tables.Where({ $_.address.address -eq $ws.dimension.address }, 'First', 1)
if ($null -eq $TableName -and $existingTable) {
$TableName = $existingTable.Name
$TableStyle = $existingTable.StyleName -replace "^TableStyle",""
$TableName = $existingTable.Name
$TableStyle = $existingTable.StyleName -replace "^TableStyle", ""
$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
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}
($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
Write-Debug -Message ("Appending: headers are " + ($script:Header -join ", ") + " Start row is $row")
if ($Title) {Write-Warning -Message "-Title Parameter is ignored when appending."}
if ($Title) { Write-Warning -Message "-Title Parameter is ignored when appending." }
}
elseif ($Title) {
#Can only add a title if not appending!
@@ -180,41 +180,41 @@
$ws.Cells[$row, $StartColumn].Value = $Title
$ws.Cells[$row, $StartColumn].Style.Font.Size = $TitleSize
if ($PSBoundParameters.ContainsKey("TitleBold")) {
if ($PSBoundParameters.ContainsKey("TitleBold")) {
#Set title to Bold face font if -TitleBold was specified.
#Otherwise the default will be unbolded.
$ws.Cells[$row, $StartColumn].Style.Font.Bold = [boolean]$TitleBold
}
if ($TitleBackgroundColor ) {
if ($TitleBackgroundColor -is [string]) {$TitleBackgroundColor = [System.Drawing.Color]::$TitleBackgroundColor }
if ($TitleBackgroundColor -is [string]) { $TitleBackgroundColor = [System.Drawing.Color]::$TitleBackgroundColor }
$ws.Cells[$row, $StartColumn].Style.Fill.PatternType = $TitleFillPattern
$ws.Cells[$row, $StartColumn].Style.Fill.BackgroundColor.SetColor($TitleBackgroundColor)
}
$row ++ ; $startRow ++
}
else { $row = $StartRow }
else { $row = $StartRow }
$ColumnIndex = $StartColumn
$Numberformat = Expand-NumberFormat -NumberFormat $Numberformat
if ((-not $ws.Dimension) -and ($Numberformat -ne $ws.Cells.Style.Numberformat.Format)) {
$ws.Cells.Style.Numberformat.Format = $Numberformat
$setNumformat = $false
$ws.Cells.Style.Numberformat.Format = $Numberformat
$setNumformat = $false
}
else { $setNumformat = ($Numberformat -ne $ws.Cells.Style.Numberformat.Format) }
else { $setNumformat = ($Numberformat -ne $ws.Cells.Style.Numberformat.Format) }
}
catch {throw "Failed preparing to export to worksheet '$WorksheetName' to '$Path': $_"}
catch { throw "Failed preparing to export to worksheet '$WorksheetName' to '$Path': $_" }
#region Special case -inputobject passed a dataTable object
<# If inputObject was passed via the pipeline it won't be visible until the process block, we will only see it here if it was passed as a parameter
if it is a data table don't do foreach on it (slow) - put the whole table in and set dates on date columns,
set things up for the end block, and skip the process block #>
if ($InputObject -is [System.Data.DataTable]) {
if ($InputObject -is [System.Data.DataTable]) {
if ($Append -and $ws.dimension) {
$row ++
$null = $ws.Cells[$row,$StartColumn].LoadFromDataTable($InputObject, $false )
if ($TableName -or $PSBoundParameters.ContainsKey('TableStyle')) {
$null = $ws.Cells[$row, $StartColumn].LoadFromDataTable($InputObject, $false )
if ($TableName -or $PSBoundParameters.ContainsKey('TableStyle')) {
Add-ExcelTable -Range $ws.Cells[$ws.Dimension] -TableName $TableName -TableStyle $TableStyle
}
}
else {
else {
#Change TableName if $TableName is non-empty; don't leave caller with a renamed table!
$orginalTableName = $InputObject.TableName
if ($PSBoundParameters.ContainsKey("TableName")) {
@@ -226,157 +226,160 @@
}
#Insert as a table, if Tablestyle didn't arrive as a default, or $TableName non-null - even if empty
if ($null -ne $TableName -or $PSBoundParameters.ContainsKey("TableStyle")) {
$null = $ws.Cells[$row,$StartColumn].LoadFromDataTable($InputObject, (-not $noHeader),$TableStyle )
$null = $ws.Cells[$row, $StartColumn].LoadFromDataTable($InputObject, (-not $noHeader), $TableStyle )
# Workaround for EPPlus not marking the empty row on an empty table as dummy row.
if ($InputObject.Rows.Count -eq 0) {
($ws.Tables | Select-Object -Last 1).TableXml.table.SetAttribute('insertRow', 1)
}
}
else {
$null = $ws.Cells[$row,$StartColumn].LoadFromDataTable($InputObject, (-not $noHeader) )
$null = $ws.Cells[$row, $StartColumn].LoadFromDataTable($InputObject, (-not $noHeader) )
}
$InputObject.TableName = $orginalTableName
}
foreach ($c in $InputObject.Columns.where({$_.datatype -eq [datetime]})) {
foreach ($c in $InputObject.Columns.where({ $_.datatype -eq [datetime] })) {
Set-ExcelColumn -Worksheet $ws -Column ($c.Ordinal + $StartColumn) -NumberFormat 'Date-Time'
}
foreach ($c in $InputObject.Columns.where({$_.datatype -eq [timespan]})) {
foreach ($c in $InputObject.Columns.where({ $_.datatype -eq [timespan] })) {
Set-ExcelColumn -Worksheet $ws -Column ($c.Ordinal + $StartColumn) -NumberFormat '[h]:mm:ss'
}
$ColumnIndex += $InputObject.Columns.Count - 1
if ($noHeader) {$row += $InputObject.Rows.Count -1 }
else {$row += $InputObject.Rows.Count }
$ColumnIndex += $InputObject.Columns.Count - 1
if ($noHeader) { $row += $InputObject.Rows.Count - 1 }
else { $row += $InputObject.Rows.Count }
$null = $PSBoundParameters.Remove('InputObject')
$firstTimeThru = $false
}
#endregion
else {$firstTimeThru = $true}
else { $firstTimeThru = $true }
}
process { if ($PSBoundParameters.ContainsKey("InputObject")) {
try {
if ($null -eq $InputObject) {$row += 1}
foreach ($TargetData in $InputObject) {
if ($firstTimeThru) {
$firstTimeThru = $false
$isDataTypeValueType = ($null -eq $TargetData) -or ($TargetData.GetType().name -match 'string|timespan|datetime|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort|URI|ExcelHyperLink')
if ($isDataTypeValueType ) {
$script:Header = @(".") # dummy value to make sure we go through the "for each name in $header"
if (-not $Append) {$row -= 1} # By default row will be 1, it is incremented before inserting values (so it ends pointing at final row.); si first data row is 2 - move back up 1 if there is no header .
process {
if ($PSBoundParameters.ContainsKey("InputObject")) {
try {
if ($null -eq $InputObject) { $row += 1 }
foreach ($TargetData in $InputObject) {
if ($firstTimeThru) {
$firstTimeThru = $false
$isDataTypeValueType = ($null -eq $TargetData) -or ($TargetData.GetType().name -match 'string|timespan|datetime|bool|byte|char|decimal|double|float|int|long|sbyte|short|uint|ulong|ushort|URI|ExcelHyperLink')
if ($isDataTypeValueType ) {
$script:Header = @(".") # dummy value to make sure we go through the "for each name in $header"
if (-not $Append) { $row -= 1 } # By default row will be 1, it is incremented before inserting values (so it ends pointing at final row.); si first data row is 2 - move back up 1 if there is no header .
}
if ($null -ne $TargetData) { Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'" }
}
if ($null -ne $TargetData) {Write-Debug "DataTypeName is '$($TargetData.GetType().name)' isDataTypeValueType '$isDataTypeValueType'" }
}
#region Add headers - if we are appending, or we have been through here once already we will have the headers
if (-not $script:Header) {
if ($DisplayPropertySet -and $TargetData.psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames) {
$script:Header = $TargetData.psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Where( {$_ -notin $ExcludeProperty})
}
else {
if ($NoAliasOrScriptPropeties) {$propType = "Property"} else {$propType = "*"}
$script:Header = $TargetData.PSObject.Properties.where( {$_.MemberType -like $propType}).Name
}
foreach ($exclusion in $ExcludeProperty) {$script:Header = $script:Header -notlike $exclusion}
if ($NoHeader) {
# Don't push the headers to the spreadsheet
$row -= 1
}
else {
$ColumnIndex = $StartColumn
foreach ($Name in $script:Header) {
$ws.Cells[$row, $ColumnIndex].Value = $Name
Write-Verbose "Cell '$row`:$ColumnIndex' add header '$Name'"
$ColumnIndex += 1
#region Add headers - if we are appending, or we have been through here once already we will have the headers
if (-not $script:Header) {
if ($DisplayPropertySet -and $TargetData.psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames) {
$script:Header = $TargetData.psStandardmembers.DefaultDisplayPropertySet.ReferencedPropertyNames.Where( { $_ -notin $ExcludeProperty })
}
else {
if ($NoAliasOrScriptPropeties) { $propType = "Property" } else { $propType = "*" }
$script:Header = $TargetData.PSObject.Properties.where( { $_.MemberType -like $propType }).Name
}
foreach ($exclusion in $ExcludeProperty) { $script:Header = $script:Header -notlike $exclusion }
if ($NoHeader) {
# Don't push the headers to the spreadsheet
$row -= 1
}
else {
$ColumnIndex = $StartColumn
foreach ($Name in $script:Header) {
$ws.Cells[$row, $ColumnIndex].Value = $Name
Write-Verbose "Cell '$row`:$ColumnIndex' add header '$Name'"
$ColumnIndex += 1
}
}
}
}
#endregion
#region Add non header values
$row += 1
$ColumnIndex = $StartColumn
<#
#endregion
#region Add non header values
$row += 1
$ColumnIndex = $StartColumn
<#
For each item in the header OR for the Data item if this is a simple Type or data table :
If it is a date insert with one of Excel's built in formats - recognized as "Date and time to be localized"
if it is a timespan insert with a built in format for elapsed hours, minutes and seconds
if its any other numeric insert as is , setting format if need be.
Preserve URI, Insert a data table, convert non string objects to string.
For strings, check for fomula, URI or Number, before inserting as a string (ignore nulls) #>
foreach ($Name in $script:Header) {
if ($isDataTypeValueType) {$v = $TargetData}
else {$v = $TargetData.$Name}
try {
if ($v -is [DateTime]) {
$ws.Cells[$row, $ColumnIndex].Value = $v
$ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = 'm/d/yy h:mm' # This is not a custom format, but a preset recognized as date and localized.
}
elseif ($v -is [TimeSpan]) {
$ws.Cells[$row, $ColumnIndex].Value = $v
$ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = '[h]:mm:ss'
}
elseif ($v -is [System.ValueType]) {
$ws.Cells[$row, $ColumnIndex].Value = $v
if ($setNumformat) {$ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat }
}
elseif ($v -is [uri] ) {
$ws.Cells[$row, $ColumnIndex].HyperLink = $v
$ws.Cells[$row, $ColumnIndex].Style.Font.Color.SetColor([System.Drawing.Color]::Blue)
$ws.Cells[$row, $ColumnIndex].Style.Font.UnderLine = $true
}
elseif ($v -isnot [String] ) { #Other objects or null.
if ($null -ne $v) { $ws.Cells[$row, $ColumnIndex].Value = $v.toString()}
}
elseif ($v[0] -eq '=') {
$ws.Cells[$row, $ColumnIndex].Formula = ($v -replace '^=','')
if ($setNumformat) {$ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat }
}
elseif ( [System.Uri]::IsWellFormedUriString($v , [System.UriKind]::Absolute) ) {
if ($v -match "^xl://internal/") {
$referenceAddress = $v -replace "^xl://internal/" , ""
$display = $referenceAddress -replace "!A1$" , ""
$h = New-Object -TypeName OfficeOpenXml.ExcelHyperLink -ArgumentList $referenceAddress , $display
$ws.Cells[$row, $ColumnIndex].HyperLink = $h
foreach ($Name in $script:Header) {
if ($isDataTypeValueType) { $v = $TargetData }
else { $v = $TargetData.$Name }
try {
if ($v -is [DateTime]) {
$ws.Cells[$row, $ColumnIndex].Value = $v
$ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = 'm/d/yy h:mm' # This is not a custom format, but a preset recognized as date and localized.
}
else {$ws.Cells[$row, $ColumnIndex].HyperLink = $v } #$ws.Cells[$row, $ColumnIndex].Value = $v.AbsoluteUri
$ws.Cells[$row, $ColumnIndex].Style.Font.Color.SetColor([System.Drawing.Color]::Blue)
$ws.Cells[$row, $ColumnIndex].Style.Font.UnderLine = $true
}
else {
$number = $null
if ( $numberRegex.IsMatch($v) -and # if it contains digit(s) - this syntax is quicker than -match for many items and cuts out slow checks for non numbers
$NoNumberConversion -ne '*' -and # and NoNumberConversion isn't specified
$NoNumberConversion -notcontains $Name -and
[Double]::TryParse($v, [System.Globalization.NumberStyles]::Any, [System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number)
) {
$ws.Cells[$row, $ColumnIndex].Value = $number
if ($setNumformat) {$ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat }
elseif ($v -is [TimeSpan]) {
$ws.Cells[$row, $ColumnIndex].Value = $v
$ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = '[h]:mm:ss'
}
elseif ($v -is [System.ValueType]) {
$ws.Cells[$row, $ColumnIndex].Value = $v
if ($setNumformat) { $ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat }
}
elseif ($v -is [uri] ) {
$ws.Cells[$row, $ColumnIndex].HyperLink = $v
$ws.Cells[$row, $ColumnIndex].Style.Font.Color.SetColor([System.Drawing.Color]::Blue)
$ws.Cells[$row, $ColumnIndex].Style.Font.UnderLine = $true
}
elseif ($v -isnot [String] ) {
#Other objects or null.
if ($null -ne $v) { $ws.Cells[$row, $ColumnIndex].Value = $v.toString() }
}
elseif ($v[0] -eq '=') {
$ws.Cells[$row, $ColumnIndex].Formula = ($v -replace '^=', '')
if ($setNumformat) { $ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat }
}
elseif ( [System.Uri]::IsWellFormedUriString($v , [System.UriKind]::Absolute) ) {
if ($v -match "^xl://internal/") {
$referenceAddress = $v -replace "^xl://internal/" , ""
$display = $referenceAddress -replace "!A1$" , ""
$h = New-Object -TypeName OfficeOpenXml.ExcelHyperLink -ArgumentList $referenceAddress , $display
$ws.Cells[$row, $ColumnIndex].HyperLink = $h
}
else { $ws.Cells[$row, $ColumnIndex].HyperLink = $v } #$ws.Cells[$row, $ColumnIndex].Value = $v.AbsoluteUri
$ws.Cells[$row, $ColumnIndex].Style.Font.Color.SetColor([System.Drawing.Color]::Blue)
$ws.Cells[$row, $ColumnIndex].Style.Font.UnderLine = $true
}
else {
$ws.Cells[$row, $ColumnIndex].Value = $v
$number = $null
if ( $numberRegex.IsMatch($v) -and # if it contains digit(s) - this syntax is quicker than -match for many items and cuts out slow checks for non numbers
$NoNumberConversion -ne '*' -and # and NoNumberConversion isn't specified
$NoNumberConversion -notcontains $Name -and
[Double]::TryParse($v, [System.Globalization.NumberStyles]::Any, [System.Globalization.NumberFormatInfo]::CurrentInfo, [Ref]$number)
) {
$ws.Cells[$row, $ColumnIndex].Value = $number
if ($setNumformat) { $ws.Cells[$row, $ColumnIndex].Style.Numberformat.Format = $Numberformat }
}
else {
$ws.Cells[$row, $ColumnIndex].Value = $v
}
}
}
catch { Write-Warning -Message "Could not insert the '$Name' property at Row $row, Column $ColumnIndex" }
$ColumnIndex += 1
}
catch {Write-Warning -Message "Could not insert the '$Name' property at Row $row, Column $ColumnIndex"}
$ColumnIndex += 1
$ColumnIndex -= 1 # column index will be the last column whether isDataTypeValueType was true or false
#endregion
}
$ColumnIndex -= 1 # column index will be the last column whether isDataTypeValueType was true or false
#endregion
}
catch { throw "Failed exporting data to worksheet '$WorksheetName' to '$Path': $_" }
}
catch {throw "Failed exporting data to worksheet '$WorksheetName' to '$Path': $_" }
}}
}
end {
if ($firstTimeThru -and $ws.Dimension) {
$LastRow = $ws.Dimension.End.Row
$LastCol = $ws.Dimension.End.Column
$endAddress = $ws.Dimension.End.Address
$LastRow = $ws.Dimension.End.Row
$LastCol = $ws.Dimension.End.Column
$endAddress = $ws.Dimension.End.Address
}
else {
$LastRow = $row
$LastCol = $ColumnIndex
$endAddress = [OfficeOpenXml.ExcelAddress]::GetAddress($LastRow , $LastCol)
$LastRow = $row
$LastCol = $ColumnIndex
$endAddress = [OfficeOpenXml.ExcelAddress]::GetAddress($LastRow , $LastCol)
}
$startAddress = [OfficeOpenXml.ExcelAddress]::GetAddress($StartRow, $StartColumn)
$dataRange = "{0}:{1}" -f $startAddress, $endAddress
$startAddress = [OfficeOpenXml.ExcelAddress]::GetAddress($StartRow, $StartColumn)
$dataRange = "{0}:{1}" -f $startAddress, $endAddress
Write-Debug "Data Range '$dataRange'"
if ($AutoNameRange) {
@@ -385,13 +388,14 @@
# 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
#using a slightly odd syntax otherwise header ends up as a 2D array
$ws.Cells[$headerRange].Value | ForEach-Object -Begin {$Script:header = @()} -Process {$Script:header += $_ }
if ($PSBoundParameters.ContainsKey($TargetData)) { #if Export was called with data that writes no header start the range at $startRow ($startRow is data)
$targetRow = $StartRow
$ws.Cells[$headerRange].Value | ForEach-Object -Begin { $Script:header = @() } -Process { $Script:header += $_ }
if ($PSBoundParameters.ContainsKey($TargetData)) {
#if Export was called with data that writes no header start the range at $startRow ($startRow is data)
$targetRow = $StartRow
}
else { $targetRow = $StartRow + 1 } #if Export was called without data to add names (assume $startRow is a header) or...
} # ... called with data that writes a header, then start the range at $startRow + 1
else { $targetRow = $StartRow + 1 }
else { $targetRow = $StartRow + 1 }
#Dimension.start.row always seems to be one so we work out the target row
#, but start.column is the first populated one and .Columns is the count of populated ones.
@@ -400,7 +404,8 @@
foreach ($c in 0..($LastCol - $StartColumn)) {
$targetRangeName = @($script:Header)[$c] #Let Add-ExcelName fix (and warn about) bad names
Add-ExcelName -RangeName $targetRangeName -Range $ws.Cells[$targetRow, ($StartColumn + $c ), $LastRow, ($StartColumn + $c )]
try {#this test can throw with some names, surpress any error
try {
#this test can throw with some names, surpress any error
if ([OfficeOpenXml.FormulaParsing.ExcelUtilities.ExcelAddressUtil]::IsValidAddress(($targetRangeName -replace '\W' , '_' ))) {
Write-Warning -Message "AutoNameRange: Property name '$targetRangeName' is also a valid Excel address and may cause issues. Consider renaming the property."
}
@@ -410,13 +415,13 @@
}
}
}
catch {Write-Warning -Message "Failed adding named ranges to worksheet '$WorksheetName': $_" }
catch { Write-Warning -Message "Failed adding named ranges to worksheet '$WorksheetName': $_" }
}
#Empty string is not allowed as a name for ranges or tables.
if ($RangeName) { Add-ExcelName -Range $ws.Cells[$dataRange] -RangeName $RangeName}
if ($RangeName) { Add-ExcelName -Range $ws.Cells[$dataRange] -RangeName $RangeName }
#Allow table to be inserted by specifying Name, or Style or both; only process autoFilter if there is no table (they clash).
if ($null -ne $TableName -or $PSBoundParameters.ContainsKey('TableStyle')) {
if ($null -ne $TableName -or $PSBoundParameters.ContainsKey('TableStyle')) {
#Already inserted Excel table if input was a DataTable
if ($InputObject -isnot [System.Data.DataTable]) {
Add-ExcelTable -Range $ws.Cells[$dataRange] -TableName $TableName -TableStyle $TableStyle
@@ -427,19 +432,19 @@
$ws.Cells[$dataRange].AutoFilter = $true
Write-Verbose -Message "Enabled autofilter. "
}
catch {Write-Warning -Message "Failed adding autofilter to worksheet '$WorksheetName': $_"}
catch { Write-Warning -Message "Failed adding autofilter to worksheet '$WorksheetName': $_" }
}
if ($PivotTableDefinition) {
foreach ($item in $PivotTableDefinition.GetEnumerator()) {
$params = $item.value
if ($Activate) {$params.Activate = $true }
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}
($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
}
@@ -453,23 +458,23 @@
$PivotTableName += 'Pivot'
}
if ($PivotTableName) {$params.PivotTableName = $PivotTableName}
else {$params.PivotTableName = $WorksheetName + 'PivotTable'}
if ($Activate) {$params.Activate = $true }
if ($PivotFilter) {$params.PivotFilter = $PivotFilter}
if ($PivotRows) {$params.PivotRows = $PivotRows}
if ($PivotColumns) {$Params.PivotColumns = $PivotColumns}
if ($PivotData) {$Params.PivotData = $PivotData}
if ($NoTotalsInPivot) {$params.PivotTotals = "None" }
Elseif ($PivotTotals) {$params.PivotTotals = $PivotTotals}
if ($PivotDataToColumn) {$params.PivotDataToColumn = $true}
if ($PivotTableName) { $params.PivotTableName = $PivotTableName }
else { $params.PivotTableName = $WorksheetName + 'PivotTable' }
if ($Activate) { $params.Activate = $true }
if ($PivotFilter) { $params.PivotFilter = $PivotFilter }
if ($PivotRows) { $params.PivotRows = $PivotRows }
if ($PivotColumns) { $Params.PivotColumns = $PivotColumns }
if ($PivotData) { $Params.PivotData = $PivotData }
if ($NoTotalsInPivot) { $params.PivotTotals = "None" }
Elseif ($PivotTotals) { $params.PivotTotals = $PivotTotals }
if ($PivotDataToColumn) { $params.PivotDataToColumn = $true }
if ($IncludePivotChart -or
$PSBoundParameters.ContainsKey('PivotChartType')) {
$params.IncludePivotChart = $true
$Params.ChartType = $PivotChartType
if ($ShowCategory) {$params.ShowCategory = $true}
if ($ShowPercent) {$params.ShowPercent = $true}
if ($NoLegend) {$params.NoLegend = $true}
$params.IncludePivotChart = $true
$Params.ChartType = $PivotChartType
if ($ShowCategory) { $params.ShowCategory = $true }
if ($ShowPercent) { $params.ShowPercent = $true }
if ($NoLegend) { $params.NoLegend = $true }
}
Add-PivotTable -ExcelPackage $pkg -SourceWorksheet $ws @params
}
@@ -513,9 +518,10 @@
}
}
}
catch {Write-Warning -Message "Failed adding Freezing the panes in worksheet '$WorksheetName': $_"}
catch { Write-Warning -Message "Failed adding Freezing the panes in worksheet '$WorksheetName': $_" }
if ($PSBoundParameters.ContainsKey("BoldTopRow")) { #it sets bold as far as there are populated cells: for whole row could do $ws.row($x).style.font.bold = $true
if ($PSBoundParameters.ContainsKey("BoldTopRow")) {
#it sets bold as far as there are populated cells: for whole row could do $ws.row($x).style.font.bold = $true
try {
if ($Title) {
$range = $ws.Dimension.Address -replace '\d+', ($StartRow + 1)
@@ -526,41 +532,41 @@
$ws.Cells[$range].Style.Font.Bold = [boolean]$BoldTopRow
Write-Verbose -Message "Set $range font style to bold."
}
catch {Write-Warning -Message "Failed setting the top row to bold in worksheet '$WorksheetName': $_"}
catch { Write-Warning -Message "Failed setting the top row to bold in worksheet '$WorksheetName': $_" }
}
if ($AutoSize -and -not $env:NoAutoSize) {
try {
#Don't fit the all the columns in the sheet; if we are adding cells beside things with hidden columns, that unhides them
if ($MaxAutoSizeRows -and $MaxAutoSizeRows -lt $LastRow ) {
$AutosizeRange = [OfficeOpenXml.ExcelAddress]::GetAddress($startRow,$StartColumn, $MaxAutoSizeRows , $LastCol)
$AutosizeRange = [OfficeOpenXml.ExcelAddress]::GetAddress($startRow, $StartColumn, $MaxAutoSizeRows , $LastCol)
$ws.Cells[$AutosizeRange].AutoFitColumns()
}
else {$ws.Cells[$dataRange].AutoFitColumns() }
else { $ws.Cells[$dataRange].AutoFitColumns() }
Write-Verbose -Message "Auto-sized columns"
}
catch { Write-Warning -Message "Failed autosizing columns of worksheet '$WorksheetName': $_"}
catch { Write-Warning -Message "Failed autosizing columns of worksheet '$WorksheetName': $_" }
}
elseif ($AutoSize) {Write-Warning -Message "Auto-fitting columns is not available with this OS configuration." }
elseif ($AutoSize) { Write-Warning -Message "Auto-fitting columns is not available with this OS configuration." }
foreach ($Sheet in $HideSheet) {
try {
$pkg.Workbook.Worksheets.Where({$_.Name -like $sheet}) | ForEach-Object {
$pkg.Workbook.Worksheets.Where({ $_.Name -like $sheet }) | ForEach-Object {
$_.Hidden = 'Hidden'
Write-verbose -Message "Sheet '$($_.Name)' Hidden."
}
}
catch {Write-Warning -Message "Failed hiding worksheet '$sheet': $_"}
catch { Write-Warning -Message "Failed hiding worksheet '$sheet': $_" }
}
foreach ($Sheet in $UnHideSheet) {
try {
$pkg.Workbook.Worksheets.Where({$_.Name -like $sheet}) | ForEach-Object {
$pkg.Workbook.Worksheets.Where({ $_.Name -like $sheet }) | ForEach-Object {
$_.Hidden = 'Visible'
Write-verbose -Message "Sheet '$($_.Name)' shown"
}
}
catch {Write-Warning -Message "Failed showing worksheet '$sheet': $_"}
catch { Write-Warning -Message "Failed showing worksheet '$sheet': $_" }
}
if (-not $pkg.Workbook.Worksheets.Where({$_.Hidden -eq 'visible'})) {
if (-not $pkg.Workbook.Worksheets.Where({ $_.Hidden -eq 'visible' })) {
Write-Verbose -Message "No Sheets were left visible, making $WorksheetName visible"
$ws.Hidden = 'Visible'
}
@@ -568,46 +574,46 @@
foreach ($chartDef in $ExcelChartDefinition) {
if ($chartDef -is [System.Management.Automation.PSCustomObject]) {
$params = @{}
$chartDef.PSObject.Properties | ForEach-Object {if ( $null -ne $_.value) {$params[$_.name] = $_.value}}
$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]) {
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. $_"}
try { [OfficeOpenXml.CalculationExtension]::Calculate($ws) }
catch { Write-Warning "One or more errors occured while calculating, save will continue, but there may be errors in the workbook. $_" }
}
if ($Barchart -or $PieChart -or $LineChart -or $ColumnChart) {
if ($NoHeader) {$FirstDataRow = $startRow}
else {$FirstDataRow = $startRow + 1 }
if ($NoHeader) { $FirstDataRow = $startRow }
else { $FirstDataRow = $startRow + 1 }
$range = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, $startColumn, $FirstDataRow, $lastCol )
$xCol = $ws.cells[$range] | Where-Object {$_.value -is [string] } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1
$xCol = $ws.cells[$range] | Where-Object { $_.value -is [string] } | ForEach-Object { $_.start.column } | Sort-Object | Select-Object -first 1
if (-not $xcol) {
$xcol = $StartColumn
$range = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, ($startColumn +1), $FirstDataRow, $lastCol )
$xcol = $StartColumn
$range = [OfficeOpenXml.ExcelAddress]::GetAddress($FirstDataRow, ($startColumn + 1), $FirstDataRow, $lastCol )
}
$yCol = $ws.cells[$range] | Where-Object {$_.value -is [valueType] -or $_.Formula } | ForEach-Object {$_.start.column} | Sort-Object | Select-Object -first 1
if (-not ($xCol -and $ycol)) { Write-Warning -Message "Can't identify a string column and a number column to use as chart labels and data. "}
$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
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" }
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
}
@@ -615,35 +621,36 @@
# 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) ) {
foreach ($c in (@() + $ConditionalText + $ConditionalFormat) ) {
try {
#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 }
$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) {
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 }
"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 -or $c.Address) {$c.Address = $ws.Dimension.Address }
elseif ($c -is [hashtable] -or $c -is [System.Collections.Specialized.OrderedDictionary]) {
if (-not $c.Range -or $c.Address) { $c.Address = $ws.Dimension.Address }
Add-ConditionalFormatting -Worksheet $ws @c
}
}
catch {throw "Error applying conditional formatting to worksheet $_"}
catch { throw "Error applying conditional formatting to worksheet $_" }
}
foreach ($s in $Style) {
if (-not $s.Range) {$s["Range"] = $ws.Dimension.Address }
if (-not $s.Range) { $s["Range"] = $ws.Dimension.Address }
Set-ExcelRange -Worksheet $ws @s
}
if ($CellStyleSB) {
@@ -652,7 +659,7 @@
$LastColumn = $ws.Dimension.Address -replace "^.*:(\w*)\d+$" , '$1'
& $CellStyleSB $ws $TotalRows $LastColumn
}
catch {Write-Warning -Message "Failed processing CellStyleSB in worksheet '$WorksheetName': $_"}
catch { Write-Warning -Message "Failed processing CellStyleSB in worksheet '$WorksheetName': $_" }
}
#Can only add password, may want to support -password $Null removing password.
@@ -661,32 +668,18 @@
$ws.Protection.SetPassword($Password)
Write-Verbose -Message 'Set password on workbook'
}
catch {throw "Failed setting password for worksheet '$WorksheetName': $_"}
catch { throw "Failed setting password for worksheet '$WorksheetName': $_" }
}
if ($PassThru) { $pkg }
if ($PassThru) { $pkg }
else {
if ($ReturnRange) {$dataRange }
if ($ReturnRange) { $dataRange }
if ($Password) { $pkg.Save($Password) }
else { $pkg.Save() }
if ($Password) { $pkg.Save($Password) }
else { $pkg.Save() }
Write-Verbose -Message "Saved workbook $($pkg.File)"
if ($ReZip) {
Write-Verbose -Message "Re-Zipping $($pkg.file) using .NET ZIP library"
try {
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+"
continue
}
try {
$TempZipPath = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName())
$null = [io.compression.zipfile]::ExtractToDirectory($pkg.File, $TempZipPath)
Remove-Item $pkg.File -Force
$null = [io.compression.zipfile]::CreateFromDirectory($TempZipPath, $pkg.File)
}
catch {throw "Error resizipping $path : $_"}
Invoke-ExcelReZipFile -ExcelPackage $pkg
}
$pkg.Dispose()

View File

@@ -35,7 +35,8 @@
[string[]]$AsText,
[string[]]$AsDate,
[ValidateNotNullOrEmpty()]
[String]$Password
[String]$Password,
[Int[]]$ImportColumns
)
end {
$sw = [System.Diagnostics.Stopwatch]::StartNew()
@@ -62,6 +63,16 @@
)
try {
if ($ImportColumns) {
$end = $Worksheet.Dimension.End.Column
# Check $ImportColumns
if ($ImportColumns[0] -le 0) { throw "The first entry in ImportColumns must be equal or greater 1" ; return }
# Check $StartColumn and $EndColumn
if (($StartColumn -ne 1) -or ($EndColumn -ne $end)) { Write-Warning -Message "If ImportColumns is set, then individual StartColumn and EndColumn will be ignored." }
# Replace $Columns with $ImportColumns
$Columns = $ImportColumns
}
if ($HeaderName) {
$i = 0
foreach ($H in $HeaderName) {
@@ -84,7 +95,7 @@
foreach ($C in $Columns) {
#allow "False" or "0" to be column headings
$Worksheet.Cells[$StartRow, $C] | Where-Object {-not [string]::IsNullOrEmpty($_.Value) } | Select-Object @{N = 'Column'; E = { $C } }, Value
$Worksheet.Cells[$StartRow, $C] | Where-Object { -not [string]::IsNullOrEmpty($_.Value) } | Select-Object @{N = 'Column'; E = { $C } }, Value
}
}
}
@@ -185,7 +196,7 @@
}
$TextColRegEx = New-Object -TypeName regex -ArgumentList $TextColExpression , 9
}
else {$TextColRegEx = $null}
else { $TextColRegEx = $null }
foreach ($R in $rows) {
#Disabled write-verbose for speed
# Write-Verbose "Import row '$R'"
@@ -194,12 +205,12 @@
foreach ($P in $PropertyNames) {
$MatchTest = $TextColRegEx.Match($P.value)
if ($MatchTest.groups.name -eq "astext") {
$NewRow[$P.Value] = $Worksheet.Cells[$R, $P.Column].Text
$NewRow[$P.Value] = $Worksheet.Cells[$R, $P.Column].Text
}
elseif ($MatchTest.groups.name -eq "asdate" -and $Worksheet.Cells[$R, $P.Column].Value -is [System.ValueType]) {
$NewRow[$P.Value] = [datetime]::FromOADate(($Worksheet.Cells[$R, $P.Column].Value))
$NewRow[$P.Value] = [datetime]::FromOADate(($Worksheet.Cells[$R, $P.Column].Value))
}
else { $NewRow[$P.Value] = $Worksheet.Cells[$R, $P.Column].Value }
else { $NewRow[$P.Value] = $Worksheet.Cells[$R, $P.Column].Value }
}
}
else {

View File

@@ -1,5 +1,6 @@
# PowerShell + Excel = Better Together
Automate Excel via PowerShell without having Excel installed. Runs on Windows, Linux and MAC. Creating Tables, Pivot Tables, Charts and much more has just become a lot easier.
<br/>
@@ -8,12 +9,6 @@ Automate Excel via PowerShell without having Excel installed. Runs on Windows, L
<br/>
Open `ImportExcel` as a remote repo in VS Code, without cloning it.
[![Open in Visual Studio Code](https://open.vscode.dev/badges/open-in-vscode.svg)](https://open.vscode.dev/dfinke/importexcel)
<br/>
| CI System | Environment | Status |
| :--- | :--- | :--- |
| Azure DevOps | Windows | [![Build Status](https://dougfinke.visualstudio.com/ImportExcel/_apis/build/status/dfinke.ImportExcel?branchName=master&jobName=Windows)](https://dougfinke.visualstudio.com/ImportExcel/_build/latest?definitionId=21&branchName=master) |

View File

@@ -1,33 +1,66 @@
#Requires -Modules Pester
if (-not (Get-command Import-Excel -ErrorAction SilentlyContinue)) {
Import-Module $PSScriptRoot\..\ImportExcel.psd1
}
Describe "Import-Excel on a sheet with no headings" {
BeforeAll {
$xlfile = "TestDrive:\testImportExcel.xlsx"
$xlfileHeaderOnly = "TestDrive:\testImportExcelHeaderOnly.xlsx"
$xl = "" | Export-excel $xlfile -PassThru
$xlfile = "$PSScriptRoot\testImportExcel.xlsx"
$xlfileHeaderOnly = "$PSScriptRoot\testImportExcelHeaderOnly.xlsx"
$xlfileImportColumns = "$PSScriptRoot\testImportExcelImportColumns.xlsx"
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A1 -Value 'A'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range B1 -Value 'B'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range C1 -Value 'C'
# Create $xlfile if it does not exist
if (!(Test-Path -Path $xlfile)) {
$xl = "" | Export-excel $xlfile -PassThru
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A2 -Value 'D'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range B2 -Value 'E'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range C2 -Value 'F'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A1 -Value 'A'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range B1 -Value 'B'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range C1 -Value 'C'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A2 -Value 'D'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range B2 -Value 'E'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range C2 -Value 'F'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A3 -Value 'G'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range B3 -Value 'H'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range C3 -Value 'I'
Close-ExcelPackage $xl
}
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A3 -Value 'G'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range B3 -Value 'H'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range C3 -Value 'I'
# Create $xlfileHeaderOnly if it does not exist
if (!(Test-Path -Path $xlfileHeaderOnly)) {
$xl = "" | Export-excel $xlfileHeaderOnly -PassThru
Close-ExcelPackage $xl
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A1 -Value 'A'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range B1 -Value 'B'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range C1 -Value 'C'
# crate $xlfileHeaderOnly
$xl = "" | Export-excel $xlfileHeaderOnly -PassThru
Close-ExcelPackage $xl
}
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A1 -Value 'A'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range B1 -Value 'B'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range C1 -Value 'C'
# Create $xlfileImportColumns if it does not exist
if (!(Test-Path -Path $xlfileImportColumns)) {
$xl = "" | Export-Excel $xlfileImportColumns -PassThru
Close-ExcelPackage $xl
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A1 -Value 'A'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range B1 -Value 'B'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range C1 -Value 'C'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range D1 -Value 'D'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range E1 -Value 'E'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range F1 -Value 'F'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A2 -Value '1'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range B2 -Value '2'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range C2 -Value '3'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range D2 -Value '4'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range E2 -Value '5'
Set-ExcelRange -Worksheet $xl.Sheet1 -Range F2 -Value '6'
Close-ExcelPackage $xl
}
}
It "Import-Excel should have this shape" {
@@ -167,7 +200,7 @@ Describe "Import-Excel on a sheet with no headings" {
}
It "Should" {
$xlfile = "TestDrive:\testImportExcelSparse.xlsx"
$xlfile = "$PSScriptRoot\testImportExcelSparse.xlsx"
$xl = "" | Export-excel $xlfile -PassThru
Set-ExcelRange -Worksheet $xl.Sheet1 -Range A1 -Value 'Chuck'
@@ -224,4 +257,139 @@ Describe "Import-Excel on a sheet with no headings" {
$actual[0].P2 | Should -Be 'B'
$actual[0].P3 | Should -Be 'C'
}
It "Should import correct data if -ImportColumns is used with the first column" {
$actual = @(Import-Excel $xlfileImportColumns -ImportColumns @(1,2,4,5))
$actualNames = $actual[0].psobject.properties.Name
$actualNames.Count | Should -Be 4
$actualNames[0] | Should -Be 'A'
$actualNames[2] | Should -Be 'D'
$actual.Count | Should -Be 1
$actual[0].A | Should -Be 1
$actual[0].B | Should -Be 2
$actual[0].D | Should -Be 4
$actual[0].E | Should -Be 5
}
It "Should import correct data if -ImportColumns is used with the first column" {
$actual = @(Import-Excel $xlfileImportColumns -ImportColumns @(1,3,4,5))
$actualNames = $actual[0].psobject.properties.Name
$actualNames.Count | Should -Be 4
$actualNames[0] | Should -Be 'A'
$actualNames[2] | Should -Be 'D'
$actual.Count | Should -Be 1
$actual[0].A | Should -Be 1
$actual[0].C | Should -Be 3
$actual[0].D | Should -Be 4
$actual[0].E | Should -Be 5
}
It "Should import correct data if -ImportColumns is used without the first column" {
$actual = @(Import-Excel $xlfileImportColumns -ImportColumns @(2,3,6))
$actualNames = $actual[0].psobject.properties.Name
$actualNames.Count | Should -Be 3
$actualNames[0] | Should -Be 'B'
$actualNames[2] | Should -Be 'F'
$actual.Count | Should -Be 1
$actual[0].B | Should -Be 2
$actual[0].C | Should -Be 3
$actual[0].F | Should -Be 6
}
It "Should import correct data if -ImportColumns is used without the first column" {
$actual = @(Import-Excel $xlfileImportColumns -ImportColumns @(2,5,6))
$actualNames = $actual[0].psobject.properties.Name
$actualNames.Count | Should -Be 3
$actualNames[0] | Should -Be 'B'
$actualNames[2] | Should -Be 'F'
$actual.Count | Should -Be 1
$actual[0].B | Should -Be 2
$actual[0].E | Should -Be 5
$actual[0].F | Should -Be 6
}
It "Should import correct data if -ImportColumns is used with only 1 column" {
$actual = @(Import-Excel $xlfile -ImportColumns @(2))
$actualNames = $actual[0].psobject.properties.Name
$actualNames.Count | Should -Be 1
$actualNames[0] | Should -Be 'B'
$actual.Count | Should -Be 2
$actual[0].B | Should -Be 'E'
}
It "Should import correct data if -ImportColumns is used with only 1 column which is also the last" {
$actual = @(Import-Excel $xlfile -ImportColumns @(3))
$actualNames = $actual[0].psobject.properties.Name
$actualNames.Count | Should -Be 1
$actualNames[0] | Should -Be 'C'
$actual.Count | Should -Be 2
$actual[1].C | Should -Be 'I'
}
It "Should import correct data if -ImportColumns contains all columns" {
$actual = @(Import-Excel $xlfileImportColumns -ImportColumns @(1,2,3,4,5,6))
$actualNames = $actual[0].psobject.properties.Name
$actualNames.Count | Should -Be 6
$actualNames[0] | Should -Be 'A'
$actualNames[2] | Should -Be 'C'
$actual.Count | Should -Be 1
$actual[0].A | Should -Be 1
$actual[0].B | Should -Be 2
$actual[0].C | Should -Be 3
$actual[0].D | Should -Be 4
$actual[0].E | Should -Be 5
$actual[0].F | Should -Be 6
}
It "Should ignore -StartColumn and -EndColumn if -ImportColumns is set aswell" {
$actual = @(Import-Excel $xlfileImportColumns -ImportColumns @(5) -StartColumn 2 -EndColumn 7)
$actualNames = $actual[0].psobject.properties.Name
$actualNames.Count | Should -Be 1
$actualNames[0] | Should -Be 'E'
$actual[0].E | Should -Be '5'
}
It "Should arrange the columns if -ImportColumns is not in order" {
$actual = @(Import-Excel $xlfileImportColumns -ImportColumns @(5,1,4))
$actualNames = $actual[0].psobject.properties.Name
$actualNames.Count | Should -Be 3
$actualNames[0] | Should -Be 'E'
$actualNames[1] | Should -Be 'A'
$actualNames[2] | Should -Be 'D'
$actual[0].E | Should -Be '5'
$actual[0].A | Should -Be '1'
$actual[0].D | Should -Be '4'
}
It "Should arrange the columns if -ImportColumns is not in order and -NoHeader is used" {
$actual = @(Import-Excel $xlfileImportColumns -ImportColumns @(5,1,4) -NoHeader -StartRow 2)
$actualNames = $actual[0].psobject.properties.Name
$actualNames.Count | Should -Be 3
$actualNames[0] | Should -Be 'P1'
$actualNames[1] | Should -Be 'P2'
$actualNames[2] | Should -Be 'P3'
$actual[0].P1 | Should -Be '5'
$actual[0].P2 | Should -Be '1'
$actual[0].P3 | Should -Be '4'
}
}

View File

@@ -1,22 +1,22 @@
#Requires -Modules Pester
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments','',Justification='False Positives')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingCmdletAliases','',Justification='Testing for presence of alias')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', Justification = 'False Positives')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingCmdletAliases', '', Justification = 'Testing for presence of alias')]
param()
describe "Consistent passing of ranges." {
BeforeAll {
$path = "TestDrive:\test.xlsx"
if (-not (Get-command Get-Service -ErrorAction SilentlyContinue)) {
Function Get-Service {Import-Clixml $PSScriptRoot\Mockservices.xml}
}
$path = "TestDrive:\test.xlsx"
# if (-not (Get-command Get-Service -ErrorAction SilentlyContinue)) {
Function Get-Service { Import-Clixml $PSScriptRoot\Mockservices.xml }
# }
}
Context "Conditional Formatting" {
Context "Conditional Formatting" {
it "accepts named ranges, cells['name'], worksheet + Name, worksheet + column " {
Remove-Item -path $path -ErrorAction SilentlyContinue
$excel = Get-Service | Export-Excel -Path $path -WorksheetName Services -PassThru -AutoSize -DisplayPropertySet -AutoNameRange -Title "Services on $Env:COMPUTERNAME"
{Add-ConditionalFormatting $excel.Services.Names["Status"] -StrikeThru -RuleType ContainsText -ConditionValue "Stopped" } | Should -Not -Throw
{ 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
{ 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) `
@@ -25,25 +25,25 @@ describe "Consistent passing of ranges." {
$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
-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 ([System.Drawing.Color]::Green) -RuleType ContainsText -ConditionValue "Running"} | Should -Not -Throw
{ Add-ConditionalFormatting "Status" -Worksheet $excel.Services `
-ForeGroundColor ([System.Drawing.Color]::Green) -RuleType ContainsText -ConditionValue "Running" } | Should -Not -Throw
$excel.Services.ConditionalFormatting.Count | Should -Be 4
Close-ExcelPackage -NoSave $excel
}
it "accepts table, table.Address and worksheet + 'C:C' " {
$excel = Get-Service | Export-Excel -Path $path -WorksheetName Services -PassThru -AutoSize -DisplayPropertySet -TableName servicetable -Title "Services on $Env:COMPUTERNAME"
{Add-ConditionalFormatting $excel.Services.Tables[0] `
-Italic -RuleType ContainsText -ConditionValue "Svc" } | Should -Not -Throw
$excel = Get-Service | Export-Excel -Path $path -WorksheetName Services -PassThru -AutoSize -DisplayPropertySet -TableName servicetable -Title "Services on $Env:COMPUTERNAME"
{ 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
{ 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 ([System.Drawing.Color]::Red) } | Should -Not -Throw
{ Add-ConditionalFormatting -Worksheet $excel.Services -Address "a:a" `
-RuleType ContainsText -ConditionValue "stopped" -ForeGroundColor ([System.Drawing.Color]::Red) } | Should -Not -Throw
$excel.Services.ConditionalFormatting.Count | Should -Be 3
Close-ExcelPackage -NoSave $excel
}
@@ -52,29 +52,29 @@ describe "Consistent passing of ranges." {
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
{ 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
{ 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
{ 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
{ 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
{ 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
{ 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
{ 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
{ 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 ([System.Drawing.Color]::Red) } | Should -Not -Throw
{ $excel.Services.Column(3) | Set-ExcelRange -FontColor ([System.Drawing.Color]::Red) } | Should -Not -Throw
$excel.Services.cells["C4"].Style.Font.Color.Rgb | Should -Be "FFFF0000"
Close-ExcelPackage -NoSave $excel
}
@@ -82,41 +82,41 @@ describe "Consistent passing of ranges." {
}
Context "PivotTables" {
it "Accepts Named range, .Cells['Name'], name&Worksheet, cells['A1:Z9'], worksheet&'A1:Z9' "{
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
$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
{ 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
{ 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
{ 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
{ 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
{ 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
{ 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
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
$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
{ 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
{ 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
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -20,6 +20,25 @@ jobs:
vmImage: 'windows-latest'
steps:
# BEGIN - ACE support for Invoke-ExcelQuery testing
- task: Cache@2
inputs:
key: v2 | "$(Agent.OS)" | ace
path: ace
cacheHitVar: CACHE_RESTORED
displayName: Cache ACE
- bash: |
mkdir ./ace
curl -o ./ace/ace.exe https://download.microsoft.com/download/3/5/C/35C84C36-661A-44E6-9324-8786B8DBE231/accessdatabaseengine_X64.exe
displayName: 'Download ACE'
condition: ne(variables.CACHE_RESTORED, 'true')
- powershell: Start-Process ./ace/ace.exe -Wait -ArgumentList "/quiet /passive /norestart"
displayName: 'Install ACE for Invoke-ExcelQuery testing'
# END - ACE support for Invoke-ExcelQuery testing
- powershell: 'Install-Module -Name Pester -Force -SkipPublisherCheck'
displayName: 'Update Pester'
- powershell: './CI/CI.ps1 -Test'
@@ -49,6 +68,25 @@ jobs:
vmImage: 'windows-latest'
steps:
# BEGIN - ACE support for Invoke-ExcelQuery testing
- task: Cache@2
inputs:
key: v2 | "$(Agent.OS)" | ace
path: ace
cacheHitVar: CACHE_RESTORED
displayName: Cache ACE
- bash: |
mkdir ./ace
curl -o ./ace/ace.exe https://download.microsoft.com/download/3/5/C/35C84C36-661A-44E6-9324-8786B8DBE231/accessdatabaseengine_X64.exe
displayName: 'Download ACE'
condition: ne(variables.CACHE_RESTORED, 'true')
- powershell: Start-Process ./ace/ace.exe -Wait -ArgumentList "/quiet /passive /norestart"
displayName: 'Install ACE for Invoke-ExcelQuery testing'
# END - ACE support for Invoke-ExcelQuery testing
- pwsh: 'Install-Module -Name Pester -Force'
displayName: 'Update Pester'
- pwsh: './CI/CI.ps1 -Test'

View File

@@ -1,3 +1,27 @@
# v7.4.2
- Thank you [James Mueller](https://github.com/jamesmmueller) Updated `ConvertFrom-ExcelToSQLInsert` to handle single quotes in the SQL statement.
- Thank you to Josh Hendricks
- Add images to spreadsheets. [Check it out](https://github.com/dfinke/ImportExcel/tree/master/Examples/AddImage)
- Catch up with him on [GitHub](https://github.com/joshooaj) and [Twitter](https://twitter.com/joshooaj) for the idea
# v7.4.1
- Implements: https://github.com/dfinke/ImportExcel/issues/1111
- Refactored ReZip into separate function
- Deletes temp folder after rezipping
- Added -ReZip to `Close-ExcelPackage`
# v7.4.0
- Thank you to [Max Goczall](https://github.com/muschebubusche) for this contribution!
- `ImportColumns` parameter added to `ImportExcel`. It is used to define which columns of the ExcelPackage should be imported.
```powershell
Import-Excel -Path $xlFile -ImportColumns @(6,7,12,25,46)
```
# v7.3.1
- Added query Excel spreadsheets, with SQL queries!

View File

@@ -1,2 +0,0 @@
# Examples

View File

@@ -1,2 +0,0 @@
# Charts

View File

@@ -1,28 +0,0 @@
# Multiplecharts
## PowerShell
```text
$xlFile = "$env:TEMP\ImportExcelExample.xlsx"
Remove-Item $xlFile -ErrorAction Ignore
$data = ConvertFrom-Csv @"
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
"@
$chart1 = New-ExcelChartDefinition -YRange "Price" -XRange "Product" -Title "Item price" -NoLegend -Height 225
$chart2 = New-ExcelChartDefinition -YRange "Total "-XRange "Product" -Title "Total sales" -NoLegend -Height 225 -Row 9 -Column 15
$chart3 = New-ExcelChartDefinition -YRange "Quantity"-XRange "Product" -Title "Sales volume" -NoLegend -Height 225 -Row 15
$data | Export-Excel -Path $xlFile -AutoFilter -AutoNameRange -AutoSize -Show -ExcelChartDefinition $chart1,$chart2,$chart3
```
## Result
![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/Examples/Charts/Multiplecharts.png)

View File

@@ -1,2 +0,0 @@
# Untitled

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB