Interlude II : Excel with PowerShell

In my last post I finished with this code:

$xl = New-Object -ComObject "Excel.Application"
$xl.visible = $true
$xlbook = $xl.workbooks.Add()
$xlbook.worksheets.Add() | out-null
$xlsheets = $xlbook.worksheets
$xlsheet1 = $xlsheets.item(1)
$xlsheet1.name = "Report"
$xlsheet1.Cells.Item(1,1) = "Header1"
$xlsheet1.Cells.Item(1,2) = "Header2"
$xlsheet1.Cells.Item(1,3) = "Header3"
$xlbook.SaveAs("REPORT.XLSX")
$xl.quit() | Out-Null

As I said, this can be made much better looking for a report. In addition, we can do things a bit smarter to make our job easier down the road.

First off, let’s break our code up into functions.

Function NewExcelObj
     {
          Set-Variable -Name xl -Value (New-Object -ComObject "Excel.Application") -Scope 1
          $xl.visible = $true
          Set-Variable -Name xlbook -Value ($xl.workbooks.Add()) -Scope 1
         $xlbook.worksheets.Add() | out-null
     }
Function AddSheets
     {
          Set-Variable -Name xlsheet -Value ($xlbook.worksheets) -Scope 1
          Set-Variable -Name xlsheet1 -Value ($xlsheet.item(1)) -Scope 1
          $xlsheet1.name = "Report"
     }
Function AddHeaders
     {
          $xlsheet1.Cells.Item(1,1) = "Header1"
          $xlsheet1.Cells.Item(1,2) = "Header2"
          $xlsheet1.Cells.Item(1,3) = "Header3"
     }
Function CloseExcel
     {
          $xlbook.SaveAs("REPORT.XLSX")
          $xl.quit() | Out-Null
     }
# Main code
NewExcelObj
AddSheets
AddHeaders
CloseExcel

What we’ve done is moved all of our code into functions. Note we had to change how certain variables are defined.

In PowerShell there are different scopes. Variables, objects, and constants reside within the scopes. Variables (note: I’m using this as shorthand to include objects and constants as well) defined in the main script are available to all levels of the script. A variable defined within a function is normally available to just that function’s scope. When the code returns from the function the variable defined within the function is lost.

Because of that you’ll see I have redone the definitions of some of the items used previously . Instead of the normal “$Variable = value” methodology I have swapped in “Set-Variable –Name Variable –Value (value) –Scope 1”. Using this format allows me to make use of the Scope option which allows me to say that the variable is defined at the scope 1 level above the function scope, in this case the base script scope level. With the variables created that way I can proceed to make use of the variables in other functions.

This cleans up the code some, but what about the spreadsheet. It’s still as ugly as before.

Let’s do something about that:

# Main code
NewExcelObj
AddSheets
[array]$headerlist = ”Header1”,”Header2”,”Header3”
AddHeaders $headerlist
CloseExcel
Function AddHeaders ($AHhl)
     {
          $col = 1
          Foreach ($t in $AHhl)
               {
                    $xlsheet1.Cells.Item(1,$col) = $t
                    $xlsheet1.Cells.Item(1,$col).Font.Bold = $True
                    $xlsheet1.Cells.Item(1,$col).Interior.ColorIndex = 15
                    $col++
               }
     }

post4

We are doing A lot of things here at once. First, just before the call of the function AddHeaders we are defining an array that contains our header values. We then pass that variable to AddHeaders – notice the line for the call of AddHeaders now is “AddHeaders $headerlist” and our Function header is “Function AddHeaders ($AHhl)” where $AHhl is contains the passed array values of $headerlist. We now have a function-local variable called $col to keep track of what column we are in. We go into a standard Foreach loop where we step through each member of $AHhl, populate the header column, and move on with incrementing $col ($col++). Between the two lines populating and incrementing are two lines that contain formatting instructions for the cells we just touched. The first sets the font in the cell to bold, the second sets a fill colour in the cell of a light grey.

That makes our headers stand out:

There’s still one more thing we should do to our sheet – autofit the columns. There is nothing more annoying to people looking at a spreadsheet than having to change the column width. We can accomplish this with just two lines of code added to our CloseExcel function.

Function CloseExcel
     {
         $UsedRange = $xlsheet1.UsedRange
         [void] $UsedRange.EntireColumn.Autofit()
         $xlbook.SaveAs("REPORT.XLSX")
         $xl.quit() | Out-Null
     }

We define a variable in the scope of the function called $UsedRange and assign the used range property of the sheet to it. After that we use the Autofit() method on the property EntireColumn forcing all of the used columns to be autofit to the data within the column. Finally we perform our previously defined quit statements, saving and closing the workbook.

Advertisements

Comments are closed.