Skip to content
ConsoleMonster

Custom views

Register how a type is shown, draft a view from sample objects, and ship views with a module as a data file with no ConsoleMonster dependency.

This page covers the view registry behind Enable-ConsoleFormatting: how to tell ConsoleMonster which columns, presenters and grouping a type gets, how to draft that from objects you already have, and how a module ships its views without depending on ConsoleMonster. Restyle any command's output walks through the same steps for one command.

Get-ContosoServer before and after its view

A module puts Contoso.Servers.consoleviews.psd1 beside its manifest: plain data naming the columns, their presenters and the grouping. The module never imports ConsoleMonster; its users get the view when they turn formatting on.

Contoso.Servers.consoleviews.psd1 85×2924-bitFull
PowerShellmodule-views.ps1View on GitHub
Import-Module ./Contoso.Servers

# Without views: PowerShell's own table
Get-ContosoServer

Enable-ConsoleFormatting

# The module's Contoso.Servers.consoleviews.psd1 picks the columns and the groups
Get-ContosoServer
Full program
Import-Module ConsoleMonster
Import-Module ./Contoso.Servers

# Without views: PowerShell's own table
Get-ContosoServer

Enable-ConsoleFormatting

# The module's Contoso.Servers.consoleviews.psd1 picks the columns and the groups
Get-ContosoServer
PowerShellContoso.Servers/Contoso.Servers.consoleviews.psd1
# Read by ConsoleMonster when it is loaded; plain data, no code. The module itself never imports ConsoleMonster.
@{
    SchemaVersion = 1
    Views         = @(
        @{
            TypeName = 'Contoso.Server'
            View     = 'Table'
            Property = @(
                'Name'
                @{ Name = 'Health'; Expression = 'State'; Presenter = 'StatusWord' }
                'Uptime'
            )
            GroupBy  = 'Site'
        }
    )
}
PowerShellContoso.Servers/Contoso.Servers.psm1
function Get-ContosoServer {
    <#
    .SYNOPSIS
    Returns the servers in the Contoso inventory as Contoso.Server objects.
    #>
    [CmdletBinding()]
    param()
    $servers = @(
        @{ Name = 'web01'; Site = 'Warsaw'; State = 'Running'; Hours = 412 }
        @{ Name = 'web02'; Site = 'Warsaw'; State = 'Running'; Hours = 9 }
        @{ Name = 'sql01'; Site = 'Warsaw'; State = 'Degraded'; Hours = 1210 }
        @{ Name = 'app01'; Site = 'Krakow'; State = 'Running'; Hours = 77 }
        @{ Name = 'app02'; Site = 'Krakow'; State = 'Stopped'; Hours = 0 }
    )
    foreach ($server in $servers) {
        [pscustomobject] @{
            PSTypeName = 'Contoso.Server'
            Name       = $server.Name
            Site       = $server.Site
            State      = $server.State
            Uptime     = New-TimeSpan -Hours $server.Hours
        }
    }
}
PowerShellContoso.Servers/Contoso.Servers.psd1
@{
    RootModule        = 'Contoso.Servers.psm1'
    ModuleVersion     = '1.0.0'
    GUID              = '5b0f7a52-6a52-4c1e-9d58-2f1f9a7c3e21'
    Author            = 'Contoso'
    Description       = 'Servers in the Contoso inventory. It knows nothing about ConsoleMonster.'
    FunctionsToExport = 'Get-ContosoServer'
}
C#module-views.host.csView on GitHub
// A compiled cmdlet: the current PowerShell runspace owns native commands and callbacks.
[Cmdlet(VerbsLifecycle.Invoke, "ModuleViewsExample") ]
public sealed class ModuleViewsExample : PSCmdlet {
    protected override void ProcessRecord() {
        InvokeCommand.InvokeScript("Import-Module ./Contoso.Servers");
        ConsoleHostViews.Import(this, "./Contoso.Servers/Contoso.Servers.consoleviews.psd1");
        using var output = new ConsoleDefaultOutput(
            this,
            new ConsoleDefaultOutputOptions { Force = true });
        foreach (var row in InvokeCommand.InvokeScript("Get-ContosoServer")) { output.Write(row); }
        output.Complete();
    }
}
Full program
using System.Management.Automation;
using ConsoleMonster.PowerShell;

// A compiled cmdlet: the current PowerShell runspace owns native commands and callbacks.
[Cmdlet(VerbsLifecycle.Invoke, "ModuleViewsExample") ]
public sealed class ModuleViewsExample : PSCmdlet {
    protected override void ProcessRecord() {
        InvokeCommand.InvokeScript("Import-Module ./Contoso.Servers");
        ConsoleHostViews.Import(this, "./Contoso.Servers/Contoso.Servers.consoleviews.psd1");
        using var output = new ConsoleDefaultOutput(
            this,
            new ConsoleDefaultOutputOptions { Force = true });
        foreach (var row in InvokeCommand.InvokeScript("Get-ContosoServer")) { output.Write(row); }
        output.Complete();
    }
}

Download HostExample.csproj beside the source file.

PowerShell 7
Import-Module ConsoleMonster
$moduleBinary = Split-Path (Get-Command Show-Console).DLL
dotnet build ./HostExample.csproj -c Release "-p:ConsoleMonsterModuleDirectory=$moduleBinary"
Import-Module ./bin/Release/net8.0/HostExample.dll
Invoke-ModuleViewsExample

This is a compiled PowerShell cmdlet class. Reference the installed module’s ConsoleMonster and ConsoleMonster.PowerShell assemblies in a PowerShell module project; ProcessRecord runs on the owning pipeline thread. Download the project beside the source file and build/run it from PowerShell 7.

C# fluentmodule-views.host.cs
// A compiled cmdlet: the current PowerShell runspace owns native commands and callbacks.
[Cmdlet(VerbsLifecycle.Invoke, "ModuleViewsExample")]
public sealed class ModuleViewsExample : PSCmdlet
{
    protected override void ProcessRecord()
    {
        InvokeCommand.InvokeScript("Import-Module ./Contoso.Servers");
        ConsoleHostViews.Import(this, "./Contoso.Servers/Contoso.Servers.consoleviews.psd1");
        using var output = new ConsoleDefaultOutput(
            this,
            new ConsoleDefaultOutputOptions().Force());
        foreach (var row in InvokeCommand.InvokeScript("Get-ContosoServer"))
        {
            output.Write(row);
        }

        output.Complete();
    }
}
Full program
using System.Management.Automation;
using ConsoleMonster.PowerShell;

// A compiled cmdlet: the current PowerShell runspace owns native commands and callbacks.
[Cmdlet(VerbsLifecycle.Invoke, "ModuleViewsExample")]
public sealed class ModuleViewsExample : PSCmdlet
{
    protected override void ProcessRecord()
    {
        InvokeCommand.InvokeScript("Import-Module ./Contoso.Servers");
        ConsoleHostViews.Import(this, "./Contoso.Servers/Contoso.Servers.consoleviews.psd1");
        using var output = new ConsoleDefaultOutput(
            this,
            new ConsoleDefaultOutputOptions().Force());
        foreach (var row in InvokeCommand.InvokeScript("Get-ContosoServer"))
        {
            output.Write(row);
        }

        output.Complete();
    }
}

Download HostExample.csproj beside the source file.

PowerShell 7
Import-Module ConsoleMonster
$moduleBinary = Split-Path (Get-Command Show-Console).DLL
dotnet build ./HostExample.csproj -c Release "-p:ConsoleMonsterModuleDirectory=$moduleBinary"
Import-Module ./bin/Release/net8.0/HostExample.dll
Invoke-ModuleViewsExample

This is a compiled PowerShell cmdlet class. Reference the installed module’s ConsoleMonster and ConsoleMonster.PowerShell assemblies in a PowerShell module project; ProcessRecord runs on the owning pipeline thread. Download the project beside the source file and build/run it from PowerShell 7.

Ship views with your module

PowerShell 5.1 and 7
# Beside YourModule.psd1:
# YourModule.consoleviews.psd1 with SchemaVersion = 1 and a Views list, as above.
# Your users:
Import-Module YourModule
Import-Module ConsoleMonster
Enable-ConsoleFormatting

See also: Register-ConsoleView

Register a view

Register-ConsoleView names a type and the columns to show for it. Once formatting is on, every object whose type names include it is shown that way when it reaches the console:

Import-Module ConsoleMonster
Enable-ConsoleFormatting

$health = @{ Name = 'Health'; Expression = { $_.State }; Presenter = 'StatusWord' }
Register-ConsoleView -TypeName Contoso.Server -Property Name, $health, Uptime -GroupBy Site
ParameterWhat it sets
-TypeNameThe type names the view applies to, as in $object.PSObject.TypeNames.
-ViewTable (the default), Cards, Tree, Inspector or Chart.
-PropertyThe columns, as Show-ConsoleTable takes them: names, or hashtables with Name, Expression, Presenter and the other column options.
-GroupByA property name or script block that puts rows under headings.
-TitleA script block giving each object's title: a shared title heads the view, different titles group the rows.
-GroupA group name: consecutive objects of types in the same group are shown together, as files and folders are.
-ShaperA script block that turns each object into what is shown instead, such as an ordered dictionary of the fields worth seeing. It applies in every ConsoleMonster command.
-SummaryA script block that sums up the objects shown together, under the view on the console. It returns facts: strings, or hashtables with Value, Label, Caption, Presenter and Role.

Registrations belong to the current session (runspace), and a registration takes precedence over the view ConsoleMonster ships for the same type. Unregister-ConsoleView -TypeName Contoso.Server removes it, and Enable-ConsoleFormatting -NoBuiltIn keeps only the views you registered.

Draft a view from samples

ConvertTo-ConsoleView looks at the objects piped to it and returns a ready-to-paste Register-ConsoleView script:

Get-Process | ConvertTo-ConsoleView

The draft examines the first 50 objects and chooses up to six non-empty columns, with the name or identity first. Presenters come from the same inference as the default table: status words, byte bars for sizes that vary more than tenfold, and relative dates for the last 30 days. A count of the objects, and of each status, becomes the summary; drop -Summary $summary to leave it out.

  • -Register installs the drafted view now, and still returns the script for later editing.
  • -AsDataFile returns a data file instead of a script, for a module to ship.

Sample one shape of object at a time. Deserialized input (from jobs and remoting) drafts for the base type with a warning, and Select-Object output keeps its Selected. type name, also with a warning.

Ship views with a module

A module can ship its views as a data file beside its manifest, named <ModuleName>.consoleviews.psd1. The module needs no dependency on ConsoleMonster: it keeps returning ordinary typed objects, and its users get the view when they import ConsoleMonster and turn formatting on.

# Contoso.Servers.consoleviews.psd1
@{
    SchemaVersion = 1
    Views = @(
        @{
            TypeName = 'Contoso.Server'
            View = 'Table'
            Property = @(
                'Name'
                @{ Name = 'Health'; Expression = 'State'; Presenter = 'StatusWord' }
                'Uptime'
            )
            GroupBy = 'Site'
            Group = 'ContosoServers'
        }
    )
}
  • Format. A PowerShell data file, read with Import-PowerShellDataFile. SchemaVersion is 1, and each entry in Views has a TypeName and optionally View, Property, GroupBy (a property name) and Group. Plain columns are strings; only columns with options need hashtables, and their Expression is a property name.
  • No code. The file holds no script blocks or commands. Calculated expressions, shapers and summaries belong in an explicit Register-ConsoleView in the session.
  • Discovery. ConsoleMonster finds the file for modules already loaded when formatting is enabled or a view is built, including modules imported after ConsoleMonster. Each module is inspected once per session, and discovery never imports other modules or changes their objects.
  • Validation. The whole file is checked before anything is registered. A discovered file that is not valid produces a warning; an explicit import reports the error.
  • Precedence. An explicit Register-ConsoleView wins over a discovered file, and discovery keeps existing registrations.

The module's users do the rest:

Import-Module Contoso.Servers
Import-Module ConsoleMonster
Enable-ConsoleFormatting
Get-ContosoServer

# Explicit formatting and export use the same registered columns.
Get-ContosoServer | New-ConsoleTable | Export-ConsoleFrame -Format Markdown

To load a file from another location, or reload one while working on it, import it explicitly:

Register-ConsoleView -Path ./views/Contoso.Servers.consoleviews.psd1

PowerShell's own Format-Table -View definitions stay separate and still come from .format.ps1xml files.

In C#

The data file is a PowerShell module convention. In .NET code, declare the same columns on the table:

var table = new Table { RecordViewRows = 0 };
table.AddColumn("Name");
table.AddColumn(new TableColumn("Health") { Presenter = CellPresenters.StatusWord });
table.AddValues("alpha", "Pass");
Terminal.Write(table);

Next

Edit this page on GitHub