StatefulMenu 1.0.3
dotnet add package StatefulMenu --version 1.0.3
NuGet\Install-Package StatefulMenu -Version 1.0.3
<PackageReference Include="StatefulMenu" Version="1.0.3" />
<PackageVersion Include="StatefulMenu" Version="1.0.3" />
<PackageReference Include="StatefulMenu" />
paket add StatefulMenu --version 1.0.3
#r "nuget: StatefulMenu, 1.0.3"
#:package StatefulMenu@1.0.3
#addin nuget:?package=StatefulMenu&version=1.0.3
#tool nuget:?package=StatefulMenu&version=1.0.3
StatefulMenu
English README
Документация: docs/ru · docs/en
Минималистичная библиотека для построения «состоящих из экранов» консольных меню на .NET с навигацией по стеку, хоткеями, локализацией и безопасным вводом данных.
Возможности
- ✅ Интерактивные меню и навигация по стеку экранов
- ✅ Хоткеи, стрелки, выбор по цифрам (многозначный), Esc для «назад», нулевой пункт
0 - ✅ Ввод моделей из консоли: обязательные/необязательные поля,
nullable - ✅ Валидация: RegEx и пользовательские валидаторы/конвертеры
- ✅ Локализация (ru/en) из коробки
- ✅ DI-расширение и авто‑регистрация команд/провайдеров меню
- ✅ Кастомный хэдер (
MenuHeaderOptions), скрытые пункты (MenuItem.Hidden), хелперыMenuItem.Back/Exit
Установка
dotnet add package StatefulMenu
Быстрый старт
- Подключите сервисы в DI и запустите навигацию с корневого меню.
using Microsoft.Extensions.DependencyInjection;
using StatefulMenu;
using StatefulMenu.Commands.Interfaces;
using StatefulMenu.Core.Interfaces;
using StatefulMenu.Core.Models;
var services = new ServiceCollection()
// Сканирует текущее сборочное дерево и регистрирует IMenuProvider/IMenuCommand
.AddStatefulMenu();
var provider = services.BuildServiceProvider();
var nav = provider.GetRequiredService<INavigationService>();
// Корневой провайдер меню (см. пример ниже)
var root = provider.GetRequiredService<IMenuProvider>();
await nav.RunAsync(root);
- Реализуйте
IMenuProviderи вернитеMenuStateс пунктамиMenuItem.
using StatefulMenu.Commands.Interfaces;
using StatefulMenu.Core.Models;
public class HomeMenuProvider : IMenuProvider
{
public Task<MenuState> CreateMenuAsync(CancellationToken ct = default)
{
var items = new[]
{
new MenuItem("Сказать привет", async _ =>
{
Console.WriteLine("Привет!");
return MenuResult.None();
}, hotkey: ConsoleKey.H),
new MenuItem("Подменю", async _ =>
{
var sub = new MenuState("Подменю", new[]
{
new MenuItem("Назад", _ => Task.FromResult(MenuResult.Pop()))
});
return MenuResult.Push(sub);
}) ,
new MenuItem("Выход", _ => Task.FromResult(MenuResult.Exit()), hotkey: ConsoleKey.E)
};
return Task.FromResult(new MenuState("Главное меню", items));
}
}
Навигация управляется значениями MenuResult:
MenuResult.None()— остаться на экранеMenuResult.Push(state)— открыть новый экранMenuResult.Replace(state)— заменить текущийMenuResult.Pop(count)— вернуться назад наcountMenuResult.Exit()— завершить цикл
Управление с клавиатуры
- Enter/цифра (1..N): выбрать пункт
- ↑/↓: перемещение по пунктам
- Esc: назад (
Pop) - Хоткеи: если у
MenuItemзаданConsoleKey, нажатие сразу выполнит его действие - 0: нулевой пункт «Назад/Выход» (можно выбирать и стрелками)
Ввод данных из консоли
Сервис IConsoleInputService позволяет запрашивать модель, помечая свойства атрибутом InputField.
using StatefulMenu.Core.Attributes;
using StatefulMenu.Core.Interfaces;
public record CreateUserModel(
[property: InputField("Email", Pattern = @"^[^@]+@[^@]+\\.[^@]+$", ErrorMessage = "Некорректный email")]
string Email,
[property: InputField("Возраст", IsRequired = false)]
int? Age,
[property: InputField("Активен")]
bool IsActive
);
// Где-то в обработчике меню
var input = provider.GetRequiredService<IConsoleInputService>();
var model = await input.ReadModelAsync<CreateUserModel>();
Поддерживается:
- Обязательные/необязательные поля (
IsRequired) - Nullable-типы
- RegEx (
Pattern+ErrorMessage) - Пользовательские валидаторы/конвертеры через
Validators/Converters(типы с публичным методомbool Validate(string input, out string? error)и конвертеры, возвращающие значение/ошибку) - Enum-поля (с подсказкой допустимых значений)
- Заголовок формы через атрибут класса
[InputModel("Заголовок")]
Локализация сообщений (ru/en) выбирается автоматически по CultureInfo.CurrentCulture.
Общие сервисы
INavigationService— управление стеком экранов, событияNavigating/NavigatedIDataService— простой ключ/значение стор для обмена данными между экранамиMenuRenderer— отрисовка текущего экрана в консоль (вьюпорт с центрированием, хэдер, 0‑пункт)
DI и авто‑регистрация
services.AddStatefulMenu():
- Регистрирует локализатор, рендерер, ввод, навигацию и стор
- Сканирует указанные сборки (или вызывающую по умолчанию) на реализации
IMenuProviderиIMenuCommandи регистрирует их
Советы по структуре
- Держите каждый экран как
IMenuProvider - Возвращайте
MenuResultиз действий пунктов меню - Для сложных сценариев храните данные в
IDataService - Для обратной совместимости: просто добавьте
BackCommand/ExitCommand/HomeCommandв список команд — они автоматически рендерятся как «0»
Примеры
- Полный демо‑проект:
temp/ClinicDemo
Требования
- .NET 9.0+
Лицензия
MIT — см. LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Microsoft.Extensions.DependencyInjection (>= 9.0.9)
- Scrutor (>= 6.1.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.3 | 228 | 12/4/2025 |
| 1.0.2 | 412 | 11/18/2025 |
| 1.0.0 | 230 | 11/4/2025 |
| 1.0.0-preview.1 | 158 | 10/2/2025 |