发布于 2026-01-06 12 阅读
0

如何利用 Spectre 构建一个外观精美的 .NET 控制台应用程序?DEV 的全球展示与讲解挑战赛由 Mux 呈现:展示你的项目!

如何使用 Spectre 构建外观精美的 .NET 控制台应用程序

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

本文将展示如何构建一个外观精美、用户乐于交互的控制台应用程序。该控制台应用程序将包含一些图形化组件,从而带来卓越的用户体验。

为什么选择控制台应用程序

应用程序种类繁多,例如移动应用、网页应用、主机应用、游戏等等。许多应用程序拥有精美的用户界面,而有些则无需用户界面即可运行。那么,主机应用——那些在终端中运行、看似几乎没有图形界面的应用程序——还有存在的必要吗?简而言之,答案是肯定的,以下是一些应用实例:

  • 脚本编写。首先,并非所有应用程序都需要用户才能运行。例如,在 CI/CD 流水线中,会有一个代理执行各种步骤。其中一个步骤可能是运行一个应用程序。这类应用程序通常是控制台应用程序,它可能会接收命令行参数作为数据输入,并对其进行操作。在这种情况下,不应该使用图形界面,因为那样会阻塞整个流程,并等待用户交互。
  • 批量处理。市面上有很多应用程序非常擅长处理批量数据,你只需要提供输入,剩下的工作就交给它们来完成。输入可以是文件目录、URL 或其他任何内容,用户无需点击任何按钮。

这是一个主机应用程序,用户界面不好也没关系吧?

人们很容易认为控制台应用程序不需要美观的界面,尤其是像我们上面提到的脚本或批处理类型的应用程序。然而,如果用户需要与之交互,某些控制台应用程序可能就需要一个更美观的界面。从用户的角度来看,这样的界面需要具备哪些要求呢?以下是一些建议:

  • 正确性。作为用户,你需要程序帮助你输入正确的数据,例如事物的正确名称,或者从有限的选项列表中选择等等。
  • 反馈。作为用户,您不仅想知道程序是否正确理解了您的输入,还想知道它当前正在执行什么操作,以及何时完成。您不希望程序什么都不说,因为您可能会将其解读为程序故障,或者需要您一直守在旁边直到它完成。
  • 阅读支持文档。您可以通过突出显示某些关键词来帮助用户,尤其是在您计划用文字描述某些内容时。这可以确保用户明白他们应该做什么。

好的,既然我们已经了解了为什么应该关注控制台应用程序的用户界面,那么我有哪些选择呢?

在本文中,我们将介绍Spectre.Console,这是一个功能非常强大的库,它不仅可以帮助我们改善各种图形控件的用户体验,还包含帮助我们处理命令行输入的方法。

参考

以下是一些可能对您有所帮助的链接:

幽灵党,高屋建瓴的概览

从宏观层面来看,Spectre.Console 主要包含两部分:

  • Spectre.Console 提供图形化组件。它包含以下功能:
    • 提示功能非常实用,它允许您提示用户输入内容,既可以提示单个输入,也可以提示从列表中选择一个或多个选项。
    • 状态、进度。通过这些信息,您可以向用户传达程序的运行情况,从而了解程序是否正常运行,是否可以离开让程序自行运行,或者程序是否卡住了。
    • 着色功能允许您为文本设置特定颜色,以确保您能够区分不同类型的消息(例如错误、日志),还可以突出显示关键字。
    • 小部件、表格、树状图、柱状图,甚至还有画布。想在游戏主机上玩吃豆人游戏的朋友们,机会来了 :)
  • 命令行处理,Spectre.Console.Cli

练习 - 你好,幽灵

接下来,我们将从快速入门指南中重点介绍的示例开始。它看起来很简单,但实际效果却非常实用。

  1. dotnet new在终端中运行以搭建 .net 项目框架。


   dotnet new console -o spectre-demo
   cd spectre-demo


Enter fullscreen mode Exit fullscreen mode
  1. 运行dotnet add package以下命令将 Spectre.Console 包添加到您的项目中:


   dotnet add package Spectre.Console


Enter fullscreen mode Exit fullscreen mode
  1. 将以下代码添加到您的Program.cs

    1. 在顶部添加 using 指令:


   using Spectre.Console;


Enter fullscreen mode Exit fullscreen mode
  1. 在下方添加以下几行:


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

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


Enter fullscreen mode Exit fullscreen mode

现在你已经有了一个使用 Spectre.Console 的程序。接下来我们运行它,看看它会发生什么:

  1. 运行以下命令dotnet run来构建和运行您的应用程序:


   dotnet run


Enter fullscreen mode Exit fullscreen mode

你应该会看到类似这样的输出文本:

幽灵输出

你现在看到的是 Spectre 对输出进行着色的功能。它的工作原理如下:

  1. 你调用该Markup()方法。它需要一个可以转换为颜色的标记作为参数。标记定义如下:


   [color format]some text[/]


Enter fullscreen mode Exit fullscreen mode

举个例子:



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


Enter fullscreen mode Exit fullscreen mode

上面的文字“Hello”将被“加下划线”并“显示为红色”。

练习 - 构建一个脚手架应用程序

到目前为止,你已经学会了如何给字符串的一部分添加颜色和其他格式。

用户输入

还有更多功能,您还可以捕获用户输入。为此,您可以使用该Confirm()方法。该方法Confirm()会显示为一个输入框,询问用户“y”或“n”。响应将以布尔值的形式存储。以下是一个示例:



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


Enter fullscreen mode Exit fullscreen mode

我们决定做一个更高级的项目,所以先清除之前的所有代码,从头开始。我们的项目将能够生成我们可能需要的 GitHub 代码库文件。

  1. 添加以下代码以提示用户:


   var answerReadme = AnsiConsole.Confirm("Generate a [green]README[/] file?");

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


Enter fullscreen mode Exit fullscreen mode

太好了,现在让我们看看效果如何,重新运行dotnet run

迅速的

好的,太好了,我们可以获取用户输入。

从选定的选项中捕获用户输入

有时您需要用户从选项列表中进行选择,以确保用户提供有效输入。为此,您可以使用选项选择器Prompt()。其原理是提供一个选项列表,并将用户选择的选项返回给您。



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"
      }));


Enter fullscreen mode Exit fullscreen mode

让我们将上述代码添加到我们的项目中,也就是添加到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"
        }));


Enter fullscreen mode Exit fullscreen mode

运行该程序后dotnet run,您应该会看到类似这样的输出:

提示选择列表

添加程序 Figlet

让我们添加一个 figlet,一个大的头部,让它感觉像一个真正的应用程序。

  1. 在 using 指令下方,添加以下代码:


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


Enter fullscreen mode Exit fullscreen mode

你目前为止的完整代码应该如下所示:



   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?");


Enter fullscreen mode Exit fullscreen mode

尝试使用以下命令运行dotnet run

Figlet 已添加

好了,现在好多了,这几乎可以算是一件值得我们骄傲的事情了 :)

处理用户输入Status()

接下来,我们需要添加代码来展示我们如何处理用户输入,为此我们可以使用Status()。它会显示为一个微调器,你可以在微调器旁边添加文本,非常炫酷,我保证 :)。

Status()大致原理如下:



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


Enter fullscreen mode Exit fullscreen mode

我在上面的代码中添加了一些注释,说明我们需要做什么。我们需要添加一个加载指示器(spinner),向用户表明任务正在执行。此外,用户已经习惯了事情并非立即完成,因此在任务之间添加一个短暂的延迟是个好主意。

现在,让我们继续进行我们的项目。

  1. 在底部添加以下代码:


   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);
    });


Enter fullscreen mode Exit fullscreen mode

让我们放大来看一段代码:



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"));
}


Enter fullscreen mode Exit fullscreen mode
  • AnsiConsole.MarkupLine()这会将任务开始时的日志消息写入日志。
  • Thread.Sleep(1000);这样可以确保任务之间有停顿。
  • ctx.Status("Next task");这是显示在旋转器旁边的文本
  • 以下代码定义了下拉列表的类型和颜色:


   ctx.Spinner(Spinner.Known.Star);
   ctx.SpinnerStyle(Style.Parse("green"));


Enter fullscreen mode Exit fullscreen mode

好了,你的代码现在应该看起来像这样:



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);
  });


Enter fullscreen mode Exit fullscreen mode

我们来试运行一下dotnet run。你应该会看到类似这样的画面:

演示

练习 - 全局安装,在所有地方运行

如今,您可能会通过 NuGet 使用应用程序。但是,有些应用程序可能并非您想共享的内容,例如公司内部应用或个人项目。有没有办法将它们安装到本地呢?简而言之,答案是肯定的。我们可以将它们打包成 NuGet 包,并在本地计算机上全局安装,这样您就可以在本地计算机上的任何位置访问它们。

要进行本地安装,我们需要执行以下步骤:

  • 告诉我们的项目文件需要打包成一个工具。
  • 将程序打包成一个工具
  • 在我们的机器上全局安装
  • 随时随地运行应用 :)

配置项目文件

我们需要告诉我们的项目,它是一个工具。

  1. 打开spectre-demo.csproj 文件,找到<PropertyGroup>并添加以下元素:


   <PackAsTool>true</PackAsTool>
   <ToolCommandName>spectre-demo</ToolCommandName>
   <PackageOutputPath>./nupkg</PackageOutputPath>


Enter fullscreen mode Exit fullscreen mode

这些元素将把我们的应用程序标识为一个工具,并将其命名为“spectre-demo”,同时定义一个输出路径,该路径指向最终生成的软件包。

  1. 在终端中运行dotnet pack以下命令创建软件包:


   dotnet pack


Enter fullscreen mode Exit fullscreen mode
  1. 运行以下命令进行全局安装:


   dotnet tool install --global --add-source ./nupkg spectre-demo


Enter fullscreen mode Exit fullscreen mode

我们成功了!让我们来测试一下,只需导航到计算机上的任意目录并运行命令即可spectre-demo。这样应该就能启动你的应用程序了。恭喜!

希望你喜欢这篇教程。如果你想看第二部分,请留言。感谢阅读 :)

概括

我们讨论了主机应用程序以及我们为什么需要它们。

然后,我们接触到了 Spectre.Console,这是一个功能非常强大的 NuGet 包,它为我们提供了从图形控件到命令行处理的各种功能。

你还学习了如何使用其中一些小部件以及如何将你的程序安装为全局命令。

这是一个仓库

文章来源:https://dev.to/dotnet/how-you-can-build-a-great-looking-net-console-app-with-spectre-2f6g