How YOU can build a great looking .NET Console app with Spectre

This article will show how to build a great looking console app that your users will love interacting with. The console app will contain some graphical widgets that will make for a great user experience.

Why console apps

There are many ty…


This content originally appeared on DEV Community and was authored by Chris Noring

This article will show how to build a great looking console app that your users will love interacting with. The console app will contain some graphical widgets that will make for a great user experience.

Why console apps

There are many types of apps, mobile apps, web apps, console apps, games and so on. Many of them have really great looking UIs some doesn't need UIs to run. So is there a need for console apps, apps that run in your terminal with seemingly little or no graphical interface? In short yes, here's soome cases:

  • Scripting. First of all, not all apps require a user to function. For example, as part of a CI/CD pipeline there's an agent that performs various steps. One of those steps could be executing an app. Such an app is usually console apps that may take command line arguments as data input and operate on those. Here, there shouldn't be a graphical interface, as it would wait stop everything up and wait for a user to interact.
  • Batch processing. There's many apps out there that are really good at working on batches of data and all it wants from you is for you to provide input and it does the rest. That input can be a file directory, a URL or something else, no need for a user to click a button.

It's a console app, it's ok if the UI is bad?

It's easy to have the opinion that a console app doesn't need a good-looking interface, especially if it's of the scripting or batch processing types we mentioned above. However, some console apps, might warrant a better-looking interface if a user is mean to interact. From a user's standpoint, what would the requirements be of a such an interface? Here's some ideas:

  • Correctness. As a user you need the program to help input the correct data, that could be the correct name of things, or from a limited list of options and so on
  • Feedback. As a user, you want to know that the program has correctly understood my input but also that it's currently working on something and when it's done doing its thing. What you don't want is a program that says nothing as you might interpret that as malfunctioning or that you need to sit and watch it until it finishes
  • Read support. You want to help the user by highlighting certain keywords, especially if you plan to describe something in text. This ensures the user understands what they're supposed to do.

Ok, so we have a case for why we should care about the user interface of a console app, what are my options?

In this article, we will look at Spectre.Console, a very competent library for helping us not only with the user experience with various graphical widgets but that also contain methods for helping us process command-line input.

References

Here's interesting links that might help you to learn more:

 Spectre, a high-level view

At high-level, Spectre.Console contains two major things:

  • Graphical widgets, Spectre.Console. Here we have features like:
    • Prompts, this is a great feature as it allows you to prompt the user for input, both single input and selection/s from a list
    • Status, Progress. By having these, you're able to convey the user how the program is doing, so you know whether you can leave the program to do it's thing if it's working or if it's stuck.
    • Coloration, this allows you to set certain colors to the text to ensure you can differentiate between different types of messages like errors, logs and also highlight keywords.
    • Widgets, Table, Tree, BarCharts and even a Canvas. Anyone wants to build Pacman in the console, here's your chance :)
  • Command line processing, Spectre.Console.Cli

Exercise - Hello Spectre

Here, we will start with the example highlighted in the quickstart. It may look simple, but what you're getting is quite useful.

  1. Run dotnet new in your terminal to scaffold a dotnet project.
   dotnet new console -o spectre-demo
   cd spectre-demo
  1. Run dotnet add package to add the Spectre.Console package to your project:
   dotnet add package Spectre.Console
  1. Add the following code to your Program.cs:

    1. At the top, add using directive:
   using Spectre.Console;
  1. Lower down, add these lines:
   AnsiConsole.Markup("[underline red]Hello[/] World!");

   AnsiConsole.Markup("[green]This is all green[/]");

Now you have a program using Spectre.Console. Lets run it next, to see what it does:

  1. Run dotnet run, to build and run your app:
   dotnet run

You should see an output looking like this text:

Spectre output

What you are seeing is Spectre's ability to color the output. It works in the following way:

  1. You call the Markup() method. It expects markup that it can turn into colors. The markup is defined like so:
   [color format]some text[/]

Here's an example:

   AnsiConsole.Markup("[underline red]Hello[/] World!");

The above text "Hello" will be made "underlined" and "red".

Exercise - build a scaffolder app

So far, you've learned how you can add colors and other formatting, to part of a string.

User input

There's more, you can also capture user input. For that you will use the Confirm() method. Confirm() will present itself as an input field asking the user for 'y' or 'n'. The response will be stored as a boolean. Here's an example:

bool saveFile = AnsiConsole.Confirm("Save file?");

Let's decide on working on a bit more advanced project, so clear all previous code and let's start fresh. Our project will be able to scaffold files we might need for a GitHub repo.

  1. Add the following code to prompt the user:
   var answerReadme = AnsiConsole.Confirm("Generate a [green]README[/] file?");

   var answerGitIgnore = AnsiConsole.Confirm("Generate a [yellow].gitignore[/] file?");

Great, now lets see what that looks like, rerun with dotnet run:

prompt

Ok, great, we can capture user input.

Capture user input from a selection

Sometimes you want the user to select from a list of options. This is to ensure the user provides valid input. For this, you can use Prompt(). The idea is to provide a list of choices. The selected choice is returned to you.

var framework = AnsiConsole.Prompt(
  new SelectionPrompt<string>()
      .Title("Select [green]test framework[/] to use")
      .PageSize(10)
      .MoreChoicesText("[grey](Move up and down to reveal more frameworks)[/]")
      .AddChoices(new[] {
          "XUnit", "NUnit","MSTest"
      }));

Let's add the above code to our project, i.e the Program.cs:

using Spectre.Console;

Console.WriteLine("");
var answerReadme = AnsiConsole.Confirm("Generate a [green]README[/] file?");
var answerGitIgnore = AnsiConsole.Confirm("Generate a [yellow].gitignore[/] file?");

var framework = AnsiConsole.Prompt(
    new SelectionPrompt<string>()
        .Title("Select [green]test framework[/] to use")
        .PageSize(10)
        .MoreChoicesText("[grey](Move up and down to reveal more frameworks)[/]")
        .AddChoices(new[] {
            "XUnit", "NUnit","MSTest"
        }));

Running the program with dotnet run, you should see something like:

prompt selection list

Adding a program Figlet

Let's add a figlet, a big header that make it feel like a real app.

  1. Just below the using directives, add this code:
   AnsiConsole.Write(
    new FigletText("Scaffold-demo")
        .LeftAligned()
        .Color(Color.Red));

Your full code so far should look like so:

   using Spectre.Console;

   AnsiConsole.Write(
    new FigletText("Scaffold-demo")
        .LeftAligned()
        .Color(Color.Red));

    Console.WriteLine("");
    var answerReadme = AnsiConsole.Confirm(
     "Generate a [green]README[/] file?");
    var answerGitIgnore = AnsiConsole.Confirm(
     "Generate a [yellow].gitignore[/] file?");

Try running it with dotnet run:

Figlet added

Alright, now that's better, it almost like something we can be proud of :)

Processing user input with Status()

Next, we want to add code to show how we are processing the user input, for that we can use Status(). It will show as a spinner and yuu can add text next to the spinner, real fancy I promise :).

Status() roughly works like so:

AnsiConsole.Status()
    .Start("Generating project...", ctx =>
    {
      // define tasks to perform
      // define spinner type
      // define spinner color
      // define delay between tasks
    })

I've written some code comments in the above code to say what we need to do. We need to add a spinner that conveys to the user that tasks are being performed. Also, the user is used to things not being instantaneous, so it's a good idea to add a slight delay between the tasks.

Now, let's keep working on our project.

  1. Add the following code at the bottom:
   AnsiConsole.Status()
    .Start("Generating project...", ctx =>
    {
      if(answerReadme) 
      {
        AnsiConsole.MarkupLine("LOG: Creating README ...");
        Thread.Sleep(1000);
        // Update the status and spinner
        ctx.Status("Next task");
        ctx.Spinner(Spinner.Known.Star);
        ctx.SpinnerStyle(Style.Parse("green"));
      }

      if(answerGitIgnore) 
      {
        AnsiConsole.MarkupLine("LOG: Creating .gitignore ...");
        Thread.Sleep(1000);
        // Update the status and spinner
        ctx.Status("Next task");
        ctx.Spinner(Spinner.Known.Star);
        ctx.SpinnerStyle(Style.Parse("green"));
      }

      // Simulate some work
      AnsiConsole.MarkupLine("LOG: Configuring test framework...");
      Thread.Sleep(2000);
    });

Let's zoom in a on a piece of code:

if(answerReadme) 
{
  AnsiConsole.MarkupLine("LOG: Creating README ...");
  Thread.Sleep(1000);
  // Update the status and spinner
  ctx.Status("Next task");
  ctx.Spinner(Spinner.Known.Star);
  ctx.SpinnerStyle(Style.Parse("green"));
}
  • AnsiConsole.MarkupLine(), this types a log message at the start of the task
  • Thread.Sleep(1000);, this ensures there's a pause between tasks
  • ctx.Status("Next task");, this is the text shown next to the spinner
  • The following code defines spinner type and spinner color:
   ctx.Spinner(Spinner.Known.Star);
   ctx.SpinnerStyle(Style.Parse("green"));

Ok, your code should now look like so:

using Spectre.Console;

AnsiConsole.Write(
new FigletText("Scaffold-demo")
    .LeftAligned()
    .Color(Color.Red));

Console.WriteLine("");
var answerReadme = AnsiConsole.Confirm("Generate a [green]README[/] file?");
var answerGitIgnore = AnsiConsole.Confirm("Generate a [yellow].gitignore[/] file?");

Console.WriteLine("");
var answerReadme = AnsiConsole.Confirm("Generate a [green]README[/] file?");
var answerGitIgnore = AnsiConsole.Confirm("Generate a [yellow].gitignore[/] file?");

var framework = AnsiConsole.Prompt(
  new SelectionPrompt<string>()
      .Title("Select [green]test framework[/] to use")
      .PageSize(10)
      .MoreChoicesText("[grey](Move up and down to reveal more frameworks)[/]")
      .AddChoices(new[] {
          "XUnit", "NUnit","MSTest"
      }));

AnsiConsole.Status()
  .Start("Generating project...", ctx =>
  {
    if(answerReadme) 
    {
      AnsiConsole.MarkupLine("LOG: Creating README ...");
      Thread.Sleep(1000);
      // Update the status and spinner
      ctx.Status("Next task");
      ctx.Spinner(Spinner.Known.Star);
      ctx.SpinnerStyle(Style.Parse("green"));
    }

    if(answerGitIgnore) 
    {
      AnsiConsole.MarkupLine("LOG: Creating .gitignore ...");
      Thread.Sleep(1000);
      // Update the status and spinner
      ctx.Status("Next task");
      ctx.Spinner(Spinner.Known.Star);
      ctx.SpinnerStyle(Style.Parse("green"));
    }

    // Simulate some work
    AnsiConsole.MarkupLine("LOG: Configuring test framework...")
;
    Thread.Sleep(2000);
  });

Let's take it for a spin with dotnet run. You should see something like:

demo

Exercise - install globally, run everywhere

Today you might consume apps from NuGet. However, some apps might not be something you want to share as it might be company internal, or they are personal projects. Is there a way to install things locally? In short, yes. We can pack up it up as NuGet package and install globally on our machine, this will make it available anywhere on your machine.

To make install locally, we need to perform the following steps:

  • Tell our project file it needs to be packed as a tool
  • Pack the program into a tool
  • Install globally on our machine
  • Run the app from anywhere :)

Configure project file

We need to tell our project it's a tool.

  1. Open spectre-demo.csproj and locate <PropertyGroup> and add the following elements:
   <PackAsTool>true</PackAsTool>
   <ToolCommandName>spectre-demo</ToolCommandName>
   <PackageOutputPath>./nupkg</PackageOutputPath>

these elements will identify our app as a tool, give it a name "spectre-demo" and define an output path where the package will end up

  1. In a terminal, run dotnet pack to create a package:
   dotnet pack
  1. Install globally by running below command:
   dotnet tool install --global --add-source ./nupkg spectre-demo

And we've done it! Let's test it out by navigating to any directory on your machine and run spectre-demo. That should start your app. Congrats!

I hope you liked this tutorial. Please comment if you want to see a part 2. Thanks for reading :)

Summary

We discussed console apps and why we need them.

You were then introduced to Spectre.Console, a very competent NuGet package giving us all kinds of capabilities from graphical widgets to command line processing.

You also learned how to use some of these widgets and install your program as a global command.

Here's a repo


This content originally appeared on DEV Community and was authored by Chris Noring


Print Share Comment Cite Upload Translate Updates
APA

Chris Noring | Sciencx (2022-02-17T00:12:00+00:00) How YOU can build a great looking .NET Console app with Spectre. Retrieved from https://www.scien.cx/2022/02/17/how-you-can-build-a-great-looking-net-console-app-with-spectre/

MLA
" » How YOU can build a great looking .NET Console app with Spectre." Chris Noring | Sciencx - Thursday February 17, 2022, https://www.scien.cx/2022/02/17/how-you-can-build-a-great-looking-net-console-app-with-spectre/
HARVARD
Chris Noring | Sciencx Thursday February 17, 2022 » How YOU can build a great looking .NET Console app with Spectre., viewed ,<https://www.scien.cx/2022/02/17/how-you-can-build-a-great-looking-net-console-app-with-spectre/>
VANCOUVER
Chris Noring | Sciencx - » How YOU can build a great looking .NET Console app with Spectre. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/02/17/how-you-can-build-a-great-looking-net-console-app-with-spectre/
CHICAGO
" » How YOU can build a great looking .NET Console app with Spectre." Chris Noring | Sciencx - Accessed . https://www.scien.cx/2022/02/17/how-you-can-build-a-great-looking-net-console-app-with-spectre/
IEEE
" » How YOU can build a great looking .NET Console app with Spectre." Chris Noring | Sciencx [Online]. Available: https://www.scien.cx/2022/02/17/how-you-can-build-a-great-looking-net-console-app-with-spectre/. [Accessed: ]
rf:citation
» How YOU can build a great looking .NET Console app with Spectre | Chris Noring | Sciencx | https://www.scien.cx/2022/02/17/how-you-can-build-a-great-looking-net-console-app-with-spectre/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.