Printing Tables from PowerShell (using WPF)

by May 17, 2018

Whenever you would like to display, print, or save as PDF in a tabular form, WPF (Windows Presentation Foundation) may be a good way. Originally, WPF was created to define user interfaces, but you can as well use it to define tables, fill in the data, and print or save them.

Don’t be put off by the amount of code. PowerShell is basically defining the table in an object-oriented way.

The example code below creates a documentation for all commands found in a module. You can easily adapt the code and create tables with more columns and other data. When you run the code below, it opens a dialog where you can choose the output printer. Choose the printer “Microsoft Print to PDF” to save the output to a PDF document.

Please note: when you run this code in the PowerShell ISE (or any other WPF-based editor), the printer dialog cannot be shown, and you’ll automatically print to the default printer. When you run the code in powershell.exe, all is fine.

# adjust the name of the module
# code will list all commands shipped by that module
# list of all modules: Get-Module -ListAvailable
$ModuleName = "PrintManagement"


# these assemblies provide access to UI
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName PresentationFramework

# create a new document
$document = [System.Windows.Documents.FlowDocument]::new()

# create a new table with two columns (20%, 80% width)
$table = [System.Windows.Documents.Table]::new()
$column1 = [System.Windows.Documents.TableColumn]::new()
$column1.Width = [System.Windows.GridLength]::new(20, [System.Windows.GridUnitType]::Star)
$table.Columns.Add($column1)
$column2 = [System.Windows.Documents.TableColumn]::new()
$column2.Width = [System.Windows.GridLength]::new(80, [System.Windows.GridUnitType]::Star)
$table.Columns.Add($column2)

# create a new rowgroup that will store the table rows
$rowGroup = [System.Windows.Documents.TableRowGroup]::new()


# produce the command data to display in the table
Get-Command -Module $moduleName | 
Get-Help | 
ForEach-Object {
    # get the information to be added to the table
    $name = $_.Name
    $synopsis = $_.Synopsis
    $description = $_.Description.Text -join ' '

    # create a new table row
    $row = [System.Windows.Documents.TableRow]::new()

    # add a cell with the command name in bold:
    $cell = [System.Windows.Documents.TableCell]::new()
    $cell.Padding = [System.Windows.Thickness]::new(0,0,10,0)
    $para = [System.Windows.Documents.Paragraph]::new()
    $inline = [System.Windows.Documents.Run]::new($name)
    $inline.FontWeight = "Bold"
    $inline.FontSize = 12
    $inline.FontFamily = "Segoe UI"
    $para.Inlines.Add($inline)
    $cell.AddChild($para)
    $row.AddChild($cell)

    # add a second cell with the command synopsis
    $cell = [System.Windows.Documents.TableCell]::new()    
    $para = [System.Windows.Documents.Paragraph]::new()
    $inline = [System.Windows.Documents.Run]::new($synopsis)
    $inline.FontSize = 12
    $inline.FontFamily = "Segoe UI"
    $para.Inlines.Add($inline)
    $cell.AddChild($para)
    $row.AddChild($cell)

    # add both cells to the table
    $rowGroup.AddChild($row)
    
    # add a second table row than spans two columns and holds the 
    # command description in a smaller font:
    $row = [System.Windows.Documents.TableRow]::new()
    $cell = [System.Windows.Documents.TableCell]::new()
    $cell.ColumnSpan = 2
    # add a 20pt gap at the bottom to separate from next command
    $cell.Padding = [System.Windows.Thickness]::new(0,0,0,20)
    $para = [System.Windows.Documents.Paragraph]::new()
    $inline = [System.Windows.Documents.Run]::new($description)
    $inline.FontSize = 10
    $inline.FontFamily = "Segoe UI"
    $para.Inlines.Add($inline)
    $cell.AddChild($para)
    $row.AddChild($cell)
    # add row to table:
    $rowGroup.AddChild($row)
}

# add all collected table rows to the table, and add the table
# to the document
$table.AddChild($rowGroup)
$document.AddChild($table)

# add a paginator that controls where pages end and new pages start:
[System.Windows.Documents.IDocumentPaginatorSource]$paginator = $document

# create a print dialog to select the printer
$printDialog = [System.Windows.Controls.PrintDialog]::new()
# print dialog can not be shown in ISE due to threading issues
# selecting a printer will work only when running this code in powershell.exe
# else, the default printer is used
try 
{
    $result = $printDialog.ShowDialog()
    if ($result -eq $false) { 
        Write-Warning "User aborted."
        return 
    }
}
catch {}

# make sure the document is not printed in multiple columns
$document.PagePadding = [System.Windows.Thickness]::new(50)
$document.ColumnGap = 0
$document.ColumnWidth = $printDialog.PrintableAreaWidth

# print the document
$printDialog.PrintDocument($paginator.DocumentPaginator, "WPF-Printing from PowerShell") 

Twitter This Tip! ReTweet this Tip!