- Bus Terminal.Gui allows you to create advanced TUI interfaces in PowerShell, with menus, dialogs and tables in text mode.
- A well-designed module can include binaries for .NET Core and .NET Framework and load them dynamically based on the PowerShell version.
- Features such as ConvertTo-DataTable, RefreshTable, or ShowWhatIf make it easy to build complete and secure TUI applications.
- With these patterns, the same TUI solution can be run in both Windows PowerShell as in PowerShell 7 with no changes to the main logic.
If you work with scripts and automation on a daily basis, sooner or later you'll want to move away from the typical black and white console. Creating TUI (Text User Interface) menus in PowerShell with the Terminal.Gui library It allows you to set up "application-like" interfaces directly in the terminal, with dialog boxes, tables, forms, and even modal windows, without resorting to WinForms or WPF.
In this article you will see, in great detail, how Bringing these TUI interfaces to both PowerShell 7 and classic Windows PowerShellWe'll cover how to correctly load binaries based on the version and how to leverage advanced controls such as credential dialogs, tables, and message boxes. We'll also review a set of example functions for managing data from a TUI, which will serve as a template for your own projects.
What is Terminal.Gui (gui.cs) and why use it with PowerShell
Terminal.Gui, also known as gui.cs is a .NET framework for creating text interfaces that run on the console. It doesn't display "classic" graphical windows, but rather text-mode interfaces, yet with menus, buttons, text boxes, tables, confirmation dialogs, etc., all perfectly navigable with the keyboard.
By default, the library has only been compiled for .NET Core, which is compatible with PowerShell 7This excludes Windows PowerShell (versions 5.1 and earlier), which uses the classic .NET Framework. As a result, many examples you see online only work in PowerShell 7 and fail in the traditional Windows console.
The key is that There is no real technical limitation preventing the use of Terminal.Gui in Windows PowerShellThe problem is solely one of compilation: the official binaries are generated against .NET Core, but the same codebase can also be compiled for .NET Framework without significant changes.
To fill this gap, a module can be created that includes Two versions of the Terminal.Gui assembly: one for .NET Core and one for .NET FrameworkThe TuiCredential module is a perfect example of this approach, because it encapsulates all the work of compiling and dynamically loading the DLLs so that you only have to call a cmdlet.

Making TUI work in PowerShell 7 and Windows PowerShell
To have Terminal.Gui available in both Windows PowerShell and PowerShell 7, you need to a set of binaries for each runtimeThat is, one assembly compiled against .NET Framework and another against .NET Core. The TuiCredential module packages both within its folder structure.
Once you have the module published (or downloaded from the PowerShell Gallery), the process of Installation is very simpleSimply use the official cmdlet for modules:
Install-Module -Name TuiCredential -Scope CurrentUser
This command can be executed both in Windows PowerShell and in PowerShell 7The magic lies in the fact that the module contains the appropriate DLLs and a script The initialization process decides which version to load based on the version you're working with. This way, the same TUI code works on both platforms without you having to duplicate logic.
The key point of the module is a file of Boot (for example, init.ps1) which, when the module is imported, is responsible for Detect the PowerShell version and load the correct Terminal.Gui assembly using Add-Type. This makes all types and classes available to the rest of the module's functions.
Conditional loading of binaries with init.ps1
When you check the module's file tree, you'll see two subfolders of binaries, typically something like Binaries (for .NET Framework) and BinariesPs7 (for .NET Core)Both contain different builds of Terminal.Gui.dll, geared towards each runtime.
Within the module, the init.ps1 script performs a very simple check with $PSVersionTable.PSVersion.Major To determine your location, if the major version is less than or equal to 5, assume you are working in Windows PowerShell and prepare the path to the .NET Framework binaries folder; otherwise, point to the folder for PowerShell 7 and .NET Core.
The basic pattern is to build a route using Join-Path with $PSScriptRootThis way, the module always locates its DLLs regardless of where it's installed. Then, the assembly name, such as Terminal.Gui.dll, is concatenated, and Add-Type -Path is called to load the type into the session.
Additionally, the script can write messages with Write-Verbose to indicate whether you are loading .NET Framework or .NET Core binariesThis is very useful when debugging compatibility issues, assembly versions, or dependency conflicts.
Thanks to this on-demand loading mechanism, all TUI code written after the import This is based on the certainty that the Terminal.Gui types are already available and compatible with the version of PowerShell in use.This avoids having to write duplicate or conditional logic in each function.
TUI Credentials Dialogue with TuiCredential
Once you have the TuiCredential module installed and the binaries loaded transparently, the most visible way to take advantage of it is through the cmdlet Show-CredentialDialog, which displays a TUI dialog box to request a username and password within the terminal.
The most basic call It's done like this:
Show-CredentialDialog
When you run it, a dialog-type window, in text mode, that requests credentialsThe username field is already populated with the current session user; you only need to enter the password and press ENTER. The cmdlet returns a PSCredential object that you can use as is for authentication in other connections, remote cmdlets, and access to... databases or any other system that requires secure credentials.
The dialogue is configurable via parametersFor example, if you want to change the window title, set a specific username, and add a button to toggle password masking, you can use something like:
Show-CredentialDialog -Title 'Log on to File Server 12' -UserName serviceaccount1 -AllowToggle
With this variant you get a dialogue better suited to the context of your scriptThis is ideal when you need to request credentials for a specific service (for example, a file server service account) instead of always using the user who is logged in.
A quick look at other TUI interfaces in PowerShell
Beyond the credentials dialog, Terminal.Gui allows mounting complete console applications with menus, forms, tables, and status barsA practical example is a "work item" or task manager, built on an SQLite database and controlled from PowerShell, which uses a series of functions to refresh forms, display help, open databases or handle action confirmations.
In these types of applications, it is common to encounter auxiliary functions for clearing and resetting forms, update the status of a bottom bar, display message boxes with WhatIf-type operations, or even open file selection dialogs with OpenDialog.
A utility function is also commonly used for Convert PowerShell object collections into DataTablesso that they can be easily linked to a Terminal.Gui TableView control. This is key for displaying data listings with columns, allowing row selection, and navigating records within the TUI.
This text-mode "mini-app" approach is very interesting when you want Replace a cumbersome WinForms GUI with a lighter, more portable, and keyboard-friendly solution, keeping all business logic in PowerShell and .NET.
Convert PowerShell data to DataTable for TableView
The function ConvertTo-DataTable is a fundamental piece when you want to populate a TableView control using data obtained from PowerShell cmdlets. The idea is to take generic objects, build a System.Data.DataTable from them, and return it as a result.
This function is usually declared with CmdletBinding, OutputType, and parameters that accept pipe inputIn the Begin block, a generic list of objects and a new DataTable called, for example, PSData are prepared. As elements are received in the Process block, they are added to the list.
At the end, in the End block, the function inspect the properties of the first object to define the columns of the DataTableFor each property, a column is created with the appropriate name and data type. Then, the list of objects is iterated through to build rows: each property is assigned to its corresponding column, and the row is added to the table.
A simple PowerShell trick allows you to return the DataTable as the sole result, instead of the rows, using the comma syntax before the object (, $table). This ensures that the returned value is the DataTable itself, ready to be bound to TableView.Table later.
To aid in debugging, the function can write messages with Write-Verbose indicating How many elements have been added and which columns are being defined?This will come in very handy when the data schema changes or when something goes wrong while trying to display the table on the screen.
TUI form management: clear, reset and fill fields
When building a TUI interface with multiple text boxes, dropdown lists, checkboxes, and labels, it's very practical to group form maintenance logic into dedicated functions such as ClearForm, ResetForm or PopulateThis way you avoid duplicating code everywhere.
The ClearForm function typically handles Empty the main input fields (name, description, progress, dates)Uncheck boxes like WhatIf, restore default values (e.g., 30 days), and reset the status bar to display the current date and the text "Ready." Finally, set the focus back to the first field and force an Application.Refresh to redraw the interface.
ResetForm goes a little further: in addition to clearing the fields, it also Restores the current path to the working database, resets table filters (for example, a checkbox to enable or disable filtering) and calls functions such as RefreshCategoryList and RefreshTable to rebuild the main list on screen.
Populate, on the other hand, takes the selected row in the TableView and Fill in the form with the details of that item.Task name, description, due date, category, and progress. Depending on whether the task is overdue, adjust the visibility of a warning label (OverDue), and you can also show or hide a checkbox that allows you to quickly clear the description.
This process also adjusts the selection of the category list, locating the appropriate index in a support list (for example, a List[string] with all the categories) and assigning it to the corresponding DropDownIn this way, the form consistently reflects the information of the active record.
Refresh tables and category lists in the interface
In a TUI application based on Terminal.Gui, it is critical to have mechanisms to dynamically update the content of the table and drop-down listsThis is where functions like RefreshTable and RefreshCategoryList come into play, working hand in hand with custom cmdlets that query the database.
RefreshTable often optionally accepts a filter category. Start by clearing the TableView control using RemoveAll and Clear to remove any trace of old dataNext, it calls a cmdlet of type Get-PSWorkItem -Path -All and apply Where-Object to filter by the desired category, selecting the relevant columns (ID, Name, Description, DueDate, Progress, Category, OverDue).
This flow of objects is passed through the ConvertTo-DataTable function, which returns a DataTable ready to be bound. The result is assigned to TableView.Table, setting the table's data source.To complete the experience, column styles are adjusted using TableView.Style.ColumnStyles.Add, controlling the alignment (for example, Right for ID or Progress, Justified for DueDate) and the minimum width.
Once the style is configured, a call is made to SetNeedsDisplay so that the controller is repainted with the new information.By running RefreshTable again after creating, modifying, or deleting tasks, the user instantly sees the updated status without having to restart the TUI application.
RefreshCategoryList, on the other hand, queries the work categories using a cmdlet like Get-PSWorkItemCategory -Path From that output, the Category property is extracted, sorted, and passed to SetSource of the dropdown control (DropDown or List, depending on the design). In addition, a generic list of strings is built that acts as a lookup table to quickly locate the index of a specific category.
It is also common to generate a small summary report with Get-PSWorkItemReport, formatting it with Format-Table Category, Count, PctTotal and Output-String. The resulting text is assigned to a label in the interface., offering at a glance a breakdown of how many tasks there are per category and what percentage they represent of the total.
Help dialogs, information, and database selection
To round out the usability of a TUI in PowerShell, it is advisable to add auxiliary dialogs to display help, application information, and to select database filesWith Terminal.Gui this is achieved quite directly.
The ShowHelp function, for example, constructs a dialog box from the contents of the Get-Help function of a specific cmdlet (such as Open-PSWorkItemConsole). It takes the cmdlet's description, passes it to an Out-String, and uses it as the dialog text. Create a Dialog object with title, width, height, and alignment., adds an “OK” button (a Terminal.Gui.Button with an Add_Clicked event that closes the dialog) and executes Application.Run on it.
To display version and dependency information, ShowAbout gathers data from various assemblies using System.Reflection.Assembly.GetAssembly(…).GetName().VersionIt usually includes the versions of PSWorkItem, PowerShell, Terminal.Gui, NStack, and System.Data.SQLite. With this, it assembles a formatted string and launches a MessageBox.Query with the title "About" and a single "OK" button.
The selection of the working database is done with the OpenDatabase function, which instantiates a OpenDialog from Terminal.Gui, configuring CanChooseDirectories, CanChooseFiles and AllowsMultipleSelection according to what you want to allow. You define an initial directory (for example, $HOME) and a list of valid extensions, such as .db.
After executing Application.Run($Dialog), it is checked that the user has not canceled and that a selected file path existsIf everything is correct, the text field that stores the path (for example, $txtPath.Text) is updated and this is used to call RefreshCategoryList and RefreshTable again, so that the application automatically starts working with the new database chosen.
To keep the user up to date with the current time, it is also common to define an UpdateStatusTime function that Update the title of the first item in the status bar with the date and timeIt executes Application.Refresh and returns a boolean value, allowing it to be hooked into a timer or periodic refresh mechanism.
Confirmations and WhatIf mode in TUI
When your TUI application allows you to create, modify, complete, or delete tasks, it's essential to incorporate some kind of confirmation before executing destructive actionsAn elegant way to do this is through a ShowWhatIf function, which generates messages such as "this is how the command would look" depending on the chosen action.
ShowWhatIf typically takes a Command parameter with a set of predefined values (New, Set, Complete, Remove) and, in some cases, a task identifier. Depending on the value received, Build a text message describing the cmdlet that would be executed (New-PSWorkItem, Set-PSWorkItem, Complete-PSWorkItem, Remove-PSWorkItem), along with relevant information: name, category, description, progress, due date, database, etc.
To calculate the expiration date, the function can check if the days control is enabled. If it is, it adds the specified number of days to the current date; otherwise, it uses the value from the explicit date field. This same pattern is reused in both the New and Set branches.ensuring that the displayed message matches the actual behavior of the cmdlet when it is run in real time.
Once the text block is assembled (formatted as a here-string for clarity), ShowWhatIf launches Terminal.Gui.MessageBox.Query with a title like “WhatIf Operation”The message and a list of buttons (usually just "OK") are included. This allows the user to calmly review what will happen before pressing the button that actually triggers the command.
This pattern integrates perfectly with the PowerShell philosophy, where the -WhatIf parameter and mock messages are very common. Bringing that same idea to the TUI reinforces user security and trust. when dealing with important or potentially destructive data.
Combining Terminal.Gui with PowerShell opens the door to developing true "console apps" with menus, dialog boxes, and tables, reusing the full power of cmdlets and .NET. With a module that manages binaries for .NET Core and .NET Framework, a DataTable conversion function, and a few routines for refreshing forms, displaying help, and confirming actions, it's possible to build very complete solutions that work in both Windows PowerShell and PowerShell 7 with minimal code changes. They make your scripts feel like real desktop applications.but at the terminal.
Passionate writer about the world of bytes and technology in general. I love sharing my knowledge through writing, and that's what I'll do on this blog, show you all the most interesting things about gadgets, software, hardware, tech trends, and more. My goal is to help you navigate the digital world in a simple and entertaining way.