Post

📖 Create Custom Console Apps using Spectre.Console

build formatted interactive console applications

📖 Create Custom Console Apps using Spectre.Console

Overview

  • Spectre.Console is
    • A .NET library that makes it easier to create beautiful console applications.
    • In Active development with good community support
    • Open-source with good Documentation
  • Requirements
    • .NET Framework 4.8 or .NET Standard 2.0 or later or .NET 8 or later

Setup

  • Create Simple C# Console Application in Visual Studio using .NET Framework 4.8
  • Add Nuget package Spectre.Console
  • Add this Sample Code in the Main method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 private static void Main()
 {
     try
     {
         //Unable UTF-8 characters to display correctly in the console
         Console.OutputEncoding = Encoding.UTF8;
         Console.InputEncoding = Encoding.UTF8;

         //Add other code snippets here
     }
     catch (Exception)
     {
         throw;
     }
     finally
     {
         Console.ReadLine();
     }
 }

Display Output

Text

1
2
//Normal Text
AnsiConsole.WriteLine("This is a normal text message");
1
2
3
4
//Text with basic formatting
AnsiConsole.MarkupLine("[bold]This is a bold text message[/]");
AnsiConsole.MarkupLine("[italic]This is an italic text message[/]");
AnsiConsole.MarkupLine("[underline]This is an underlined text message[/]");
1
2
3
4
//Color the output text in the console
AnsiConsole.MarkupLine("[green]Hello, World![/]");
AnsiConsole.MarkupLine("[red]This is an error message[/]");
AnsiConsole.MarkupLine("[yellow]This is a warning message[/]");
1
2
//Use Hexadecimal color codes to color the output text in the console
AnsiConsole.MarkupLine("[#8F00FF]This is a custom color message[/]");
1
2
//Combine formatting and color
AnsiConsole.MarkupLine("[bold green]This is a bold and green text message[/]");
1
2
3
//Text with background color
AnsiConsole.MarkupLine("[black on yellow]This is a text message with a yellow background[/]");
AnsiConsole.MarkupLine("[red on white bold]This is a text message with a white background and red text[/]");
1
2
3
4
5
//Text with effects
AnsiConsole.MarkupLine("[blink]This is a blinking text message[/]");
AnsiConsole.MarkupLine("[slowblink]This is a blinking text message[/]");
AnsiConsole.MarkupLine("[reverse]This is a reversed text message[/]");
AnsiConsole.MarkupLine("[conceal]This is a concealed text message[/]");

Text Style

1
2
3
//Using Text Style
var style = new Style(foreground: Color.Green, background: Color.Black, decoration: Decoration.Bold | Decoration.Underline);
AnsiConsole.Write(new Markup("This is a text message with a custom style",style));

Title

1
2
 //Title
 AnsiConsole.Write(new FigletText("Hello, World!").Centered().Color(Color.Green));

URL

1
AnsiConsole.MarkupLine("→ Website: [link=https://nodesautomations.com]Nodes Automations[/]");

Exception

1
AnsiConsole.WriteException(ex,ExceptionFormats.NoStackTrace);

Table

  • Simple Table with default settings
1
2
3
4
5
6
7
8
9
10
11
Table table = new Table();
table.AddColumn("Beam Id");
table.AddColumn("Width");
table.AddColumn("Depth");

table.AddRow("1", "230", "300");
table.AddRow("2", "230", "350");
table.AddRow("3", "230", "400");

AnsiConsole.WriteLine();
AnsiConsole.Write(table);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Table with custom settings
table.AddColumns("Beam Id", "Width", "Depth");

table.AddRow("1", "230", "300");
table.AddRow("2", "230", "350");
table.AddRow("3", "230", "400");

//Add Formatting to the table
table.Border(TableBorder.Rounded);
table.ShowRowSeparators();

//Adjust Column formatting
table.Columns[0].LeftAligned();
table.Columns[1].Centered();
table.Columns[2].Centered();

Tree

1
2
3
4
5
6
7
8
//Simple Tree
var tree = new Tree("Data");

tree.AddNode("Story Data");
tree.AddNode("Section Data");
tree.AddNode("Design Data");

AnsiConsole.Write(tree);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Tree with nested nodes
var tree = new Tree("Data");

var storyData = tree.AddNode("Story Data");

var sectionData= tree.AddNode("Section Data");
sectionData.AddNode("Beam Sections");
sectionData.AddNode("Column Sections");
sectionData.AddNode("Slab Sections");
sectionData.AddNode("Wall Sections");

var designData = tree.AddNode("Design Data");
designData.AddNode("Beam Design");
designData.AddNode("Column Design");
designData.AddNode("Slab Design");

AnsiConsole.Write(tree);

Status Update

1
2
3
4
5
6
7
8
AnsiConsole.Status()
    .Start("Extracting Data From Model...", ctx =>
    {
        // Simulate some work
        Thread.Sleep(3000);
    });

AnsiConsole.MarkupLine("[green]Data Extraction Done![/]");
1
2
3
4
5
6
7
8
9
10
11
12
13
//Status messages with spinner
await AnsiConsole.Status()
    .StartAsync("Processing...", async ctx =>
    {
        ctx.Spinner(Spinner.Known.Dots);
        ctx.SpinnerStyle(Style.Parse("green"));
        //Simulate some work
        await Task.Delay(3000);
        ctx.Status("Almost done...");
        await Task.Delay(2000);
    });

AnsiConsole.WriteLine("Processing Done");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//Spinner with multiple status messages and custom spinner type and color
AnsiConsole.Status()
    .Spinner(Spinner.Known.Dots)//Change the spinner type
    .SpinnerStyle(Style.Parse("red"))//Change the spinner color
    .Start("Extrating data from model...", ctx =>
    {
        Thread.Sleep(1500);

        ctx.Status("Extracting Story Data...");
        Thread.Sleep(2000);

        ctx.Status("Extracting Section Data...");
        Thread.Sleep(2000);

        ctx.Status("Extracting Results...");
        Thread.Sleep(2000);

        ctx.Status("Extracting Design Data...");
        Thread.Sleep(2000);

        ctx.Spinner(Spinner.Known.Arc);//Change the spinner type
        ctx.SpinnerStyle(Style.Parse("green"));//Change the spinner color
        ctx.Status("Finalizing...");
        Thread.Sleep(1000);
    });

AnsiConsole.MarkupLine("[green]Data Extraction Done![/]");

Chart

1
2
3
4
5
6
AnsiConsole.WriteLine("Max Movement = 100");
AnsiConsole.Write(new BarChart()
    .Label("[green]Junction[/]")
    .AddItem("Top", 85, Color.Blue)
    .AddItem("Middle", 62, Color.Yellow)
    .AddItem("Bottom", 100, Color.Green));
1
2
3
4
5
6
AnsiConsole.WriteLine();
AnsiConsole.WriteLine("Overall Cost is 100");
AnsiConsole.Write(new BreakdownChart()
    .AddItem("Material", 65, Color.Green)
    .AddItem("Labor", 25, Color.Blue)
    .AddItem("Overhead", 10, Color.Yellow));

Progress Bar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 //Progress Bar
 AnsiConsole.Progress()
    .AutoClear(false) // Do not clear the progress bar when done
    .Columns(new ProgressColumn[]
    {
        new TaskDescriptionColumn(),    // Task description
        new ProgressBarColumn(),         // Progress bar
        new PercentageColumn(),          // Percentage
        new RemainingTimeColumn(),       // Remaining time
        new SpinnerColumn(),             // Spinner
    })
    .Start(ctx =>
    {
        var task1 = ctx.AddTask("[green]Processing task 1[/]");
        var task2 = ctx.AddTask("[yellow]Processing task 2[/]");
        var task3 = ctx.AddTask("[red]Processing task 3[/]");
        while (!ctx.IsFinished)
        {
            task1.Increment(0.5);
            task2.Increment(0.3);
            task3.Increment(0.2);
            Task.Delay(100).Wait();
        }
    });

Get User Input

Simple User Inputs

  • It will automatically validate the input type and prompt the user again if the input is invalid.
1
2
3
4
string name = AnsiConsole.Ask<string>("What is your name?");//Require string input from the user
int age= AnsiConsole.Ask<int>("What is your age?");//Require integer input from the user
bool isHappy = AnsiConsole.Confirm("Are you happy?");//Require Y/N input from the user
bool isMale = AnsiConsole.Ask<bool>("Are you male?");//Require True/False input from the user

Get User Input custom validation

  • You can sepecify any data type and provide custom validation logic to validate the user input.
1
2
3
4
var selectedDia = AnsiConsole.Prompt(
    new TextPrompt<int>("Select a Dia:")
    .AddChoices(new[] { 8, 10, 12 })
    .DefaultValue(8));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 //Prompt with logic for handling user input
 var age = AnsiConsole.Prompt(
    new TextPrompt<int>("Enter your age:")
    .Validate(age =>
    {
    if (age < 0)
        {
            return ValidationResult.Error("[red]You cannot enter a negative age[/]");
        }
        else if (age > 120)
        {
            return ValidationResult.Error("[red]You cannot enter an age greater than 120[/]");
        }
        else
        {
            return ValidationResult.Success();
        }
    }));

Select from a List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
List<string> colorList = new List<string>() { "Red", "Green", "Blue", "Yellow", "Purple", "Cyan", "White", "Black" };
string selectedColor = AnsiConsole.Prompt(
    new SelectionPrompt<string>()
    .Title("Select a color:")
    .AddChoices(colorList));

AnsiConsole.WriteLine($"Your selected color is {selectedColor}");

string selectedColor2 = AnsiConsole.Prompt(
    new SelectionPrompt<string>()
    .Title("Select a color:")
    .PageSize(5) //Only show 5 options at a time, if there are more than 5 options, the user can scroll through the list
    .MoreChoicesText("[grey](Move up and down to reveal more colors)[/]") //Text to show when there are more options than the page size
    .AddChoices(colorList));

AnsiConsole.WriteLine($"Your selected color is {selectedColor2}");

Multi-Select from a List

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Multi select from a list of options
List<string> colorList = new List<string>() { "Red", "Green", "Blue", "Yellow", "Purple", "Cyan", "White", "Black" };

var selectedColors = AnsiConsole.Prompt(
    new MultiSelectionPrompt<string>()
    .Title("Select one or more colors:")
    .AddChoices(colorList));

AnsiConsole.WriteLine($"Your selected colors are {string.Join(", ", selectedColors)}");

var selectedColors2 = AnsiConsole.Prompt(
    new MultiSelectionPrompt<string>()
    .Title("Select one or more colors:")
    .PageSize(5) //Only show 5 options at a time, if there are more than 5 options, the user can scroll through the list
    .InstructionsText("[grey](Press [blue]<space>[/] to toggle a color, [green]<enter>[/] to accept)[/]") //Instructions for the user
    .AddChoices(colorList));

AnsiConsole.WriteLine($"Your selected colors are {string.Join(", ", selectedColors2)}");
1
2
3
4
5
6
7
8
9
10
11
12
13
//Multi select from Grouped list of options
List<string> colorList = new List<string>() { "Red", "Green", "Blue", "Yellow", "Purple", "Cyan", "White", "Black" };
List<string> CustomColors = new List<string>() {"Cherry Red","Minty Green", "Ocean Blue", "Sunset Orange" , "Lemon Yellow" };

var selectedColors3 = AnsiConsole.Prompt(
    new MultiSelectionPrompt<string>()
    .Title("Select one or more colors:")
    .PageSize(5) //Only show 5 options at a time, if there are more than 5 options, the user can scroll through the list
    .InstructionsText("[grey](Press [blue]<space>[/] to toggle a color, [green]<enter>[/] to accept)[/]") //Instructions for the user
    .AddChoiceGroup("Basic Colors", colorList)
    .AddChoiceGroup("Custom Colors", CustomColors));

AnsiConsole.WriteLine($"Your selected colors are {string.Join(", ", selectedColors3)}");

Conclusion

  • Spectre.Console is perfect for creating interactive console applications with formatted output and user input handling.
  • It has all the features you need to create a fully functional console application with a good user experience.
  • Not suitable when you need lot of predefined inputs or a complex user interface, for that best to use a GUI framework like WPF or WinForms.
This post is licensed under CC BY-NC-ND 4.0 by the author.