Creating Out-TreeView using Terminal.GUI and PowerShell

Image Description

Daily PowerShell #45

Daily PowerShell

November 30, 2021

quote Discuss this Article

In this post, we’ll look at how to create a Out-TreeView cmdlet with Terminal.GUI.

What is GUI.CS (Terminal.Gui)?

The GUI.CS project is a terminal interface library for .NET. It has many controls for building user interfaces in .NET applications. We use this library for the TUI Designer.

It has controls such as:

Using GUI.CS in PowerShell

To use GUI.CS in PowerShell, you will need to download the appropriate binaries from NuGet. NuGet packages are just ZIP files. You can download an rename them to ZIP to access the files within them.

Invoke-WebRequest https://www.nuget.org/api/v2/package/Terminal.Gui/1.3.1 -OutFile .\terminal.gui.zip

Within the lib folder you will find binaries for different platforms. For the purposes of this blog post, we’ll use the netstandard2.0 binary.

You will also need the NStack library as Terminal.Gui relies on it.

Invoke-WebRequest https://www.nuget.org/api/v2/package/NStack.Core/0.16.0 -OutFile .\nstack.zip

Once the binaries have been downloaded, you will need to copy the NStack.dll and the Terminal.Gui.dll into a folder. You can load the binaries using Import-Module and call the Terminal.Gui classes from PowerShell.

[Terminal.Gui.Application]::Init()
$Top = [Terminal.Gui.Application]::Top

$Win = [Terminal.Gui.Window]::new("Hello, World!")
$Win.Height = [Terminal.Gui.Dim]::Fill()
$Win.Width = [Terminal.Gui.Dim]::Fill()

$Top.Add($Win)
[Terminal.Gui.Application]::Run()

Creating the Out-TreeView Cmdlet

The Out-TreeView cmdlet will use the Terminal.Gui classes and pipeline input in PowerShell to output a treeview based on the objects defined.

The following script loads the binaries and defines the Out-TreeView cmdlet to output a tree view based on objects.

Import-Module "$PSScriptRoot\Gui\NStack.dll" | Out-Null
Import-Module "$PSScriptRoot\Gui\Terminal.Gui.dll" | Out-Null

function Expand-Object {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [object]$InputObject
    )

    Process {
        if ($InputObject -eq $null) {
            return
        }
        $InputObject | Get-Member -MemberType Properties | ForEach-Object {
            try {
                $Value = $InputObject.($_.Name)
                $Node = [Terminal.Gui.Trees.TreeNode]::new("$($_.Name) = $Value")

                if ($Value -ne $null) {
                    $Children = Expand-Object -InputObject $Value
                    foreach ($child in $Children) {
                        $Node.Children.Add($child)
                    }
                }

                $Node
            }
            catch {
                Write-Host $_
            }
        }

    }
}

function Out-TreeView {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [object]$InputObject
    )

    Begin {
        $Objects = @() 
    }
    
    Process {
        $Objects += $InputObject
    }

    End {
        [Terminal.Gui.Application]::Init()
        $Top = [Terminal.Gui.Application]::Top

        $Win = [Terminal.Gui.Window]::new("Out-TreeView")
        $Win.Height = [Terminal.Gui.Dim]::Fill()
        $Win.Width = [Terminal.Gui.Dim]::Fill()

        $TreeView = [Terminal.Gui.TreeView]::new()
        $TreeView.Height = [Terminal.Gui.Dim]::Fill()
        $TreeView.Width = [Terminal.Gui.Dim]::Fill()

        foreach ($item in $Objects) {
            $root = [Terminal.Gui.Trees.TreeNode]::new($item.GetType().Name)
            $Children = Expand-Object $item
            $Children | ForEach-Object {
                $root.Children.Add($_)
            }
            $TreeView.AddObject($root)
        }

        $Win.Add($TreeView)

        $Top.Add($Win)
 
        [Terminal.Gui.Application]::Run()
    }
}

Use the Out-TreeView Cmdlet

Once you’ve defined the above cmdlet, you can use it on the pipeline.

Get-Process | Select-Object -First 10 | Out-TreeView

Conclusion

The Out-TreeView cmdlet can be used to view complex objects using Terminal.Gui. The cmdlet likely could be improved to increase performance and lazy load properties.