A fluent, type-safe user input handler and validation library for .NET with platform-specific implementations for Console, WinForms, WPF, Avalonia, MAUI, and Blazor.
- π Features
- π¦ Installation
- π― Quick Start
- π Usage Guide
- π₯οΈ Platform Guide
- ποΈ Architecture
- π Extending TypeGuard
- π» Requirements
- π License
- π€ Contributing
- Type-Safe Validation - Built-in input handlers + validators for all common C# types
- Composable Rules - Chain validation rules, similarly to LINQ
- Async Support - Full async/await support with cancellation tokens
- Extensible - Add custom handlers and rules with ease
- Multi-Platform - Dedicated implementations for Console, WinForms, WPF, Avalonia, MAUI, and Blazor
Install only the package for your platform. TypeGuard.Core is included automatically as a dependency.
dotnet add package TypeGuard.Consoledotnet add package TypeGuard.Winformsdotnet add package TypeGuard.Wpfdotnet add package TypeGuard.Avaloniadotnet add package TypeGuard.Mauidotnet add package TypeGuard.Blazor(Note: ASP.NET are planned but isn't fully ready yet)
dotnet add reference path/to/TypeGuard.Console/TypeGuard.Console.csproj # or your platform projectusing TypeGuard.Console;
int age = Guard.Int("Enter your age")
.WithRange(1, 120)
.Get();
string name = await Guard.String("Enter your name")
.WithNoDigits()
.WithLengthRange(2, 50)
.GetAsync();using TypeGuard.Winforms;
private readonly Guard _guard;
public MyForm()
{
InitializeComponent();
_guard = new Guard(inputTextBox, promptLabel, errorLabel);
}
private async void submitButton_Click(object sender, EventArgs e)
{
int age = await _guard.Int("Enter your age")
.WithRange(1, 120)
.GetAsync();
}int count = await Guard.Int("How many items?").GetAsync();
string username = await Guard.String("Enter username").GetAsync();
DateTime date = await Guard.DateTime("Enter a date", "dd/MM/yyyy").GetAsync();int age = await Guard.Int("Enter your age")
.WithRange(18, 120)
.GetAsync();
string username = await Guard.String("Choose a username")
.WithLengthRange(3, 20)
.WithNoDigits()
.GetAsync();string password = await Guard.String("Create a password")
.WithLengthRange(8, null)
.WithRegex(@"[A-Z]", "Must contain at least one uppercase letter")
.WithRegex(@"[a-z]", "Must contain at least one lowercase letter")
.WithRegex(@"\d", "Must contain at least one digit")
.WithCustomRule(
p => p.Distinct().Count() >= 5,
"Must contain at least 5 unique characters")
.GetAsync();DateTime appointment = await Guard.DateTime("Enter appointment date", "dd/MM/yyyy")
.WithRange(DateTime.Today, DateTime.Today.AddMonths(6))
.GetAsync();
DateTime birthday = await Guard.DateTime("Enter birthday")
.WithCustomRule(
d => DateTime.Today.Year - d.Year >= 18,
"Must be 18 or older")
.GetAsync();
DateTime meeting = await Guard.DateTime("Select meeting date", "yyyy-MM-dd")
.WithWeekday()
.GetAsync();using TypeGuard.Console;
int age = await Guard.Int("Enter your age")
.WithRange(1, 120)
.GetAsync();
string name = await Guard.String("Enter your name").GetAsync();using TypeGuard.Winforms;
private readonly Guard _guard;
public MyForm()
{
InitializeComponent();
_guard = new Guard(inputTextBox, promptLabel, errorLabel);
}
private async void submitButton_Click(object sender, EventArgs e)
{
int age = await _guard.Int("Enter your age")
.WithRange(1, 120)
.GetAsync();
}using TypeGuard.Wpf;
private readonly Guard _guard;
public MyWindow()
{
InitializeComponent();
_guard = new Guard(inputTextBox, promptBlock, errorBlock);
}
private async void SubmitButton_Click(object sender, RoutedEventArgs e)
{
int age = await _guard.Int("Enter your age")
.WithRange(1, 120)
.GetAsync();
}using TypeGuard.Avalonia;
private readonly Guard _guard;
public MyView()
{
InitializeComponent();
_guard = new Guard(InputTextBox, PromptBlock, ErrorBlock);
}
private async void SubmitButton_Click(object sender, RoutedEventArgs e)
{
int age = await _guard.Int("Enter your age")
.WithRange(1, 120)
.GetAsync();
}using TypeGuard.Maui;
_guard = new Guard(InputEntry, PromptLabel, ErrorLabel, SubmitButton);
int age = await _guard.Int("Enter your age")
.WithRange(1, 120)
.GetAsync();@inject Guard Guard
<InputText @bind-Value="Guard.Input.Value" />
<p>@Guard.Output.PromptMessage</p>
<p style="color: red">@Guard.Output.ErrorMessage</p>
@code {
protected override void OnInitialized()
{
Guard.Output.OnStateChanged = StateHasChanged;
}
private async Task SubmitAsync()
{
int age = await Guard.Int("Enter your age")
.WithRange(1, 120)
.GetAsync();
}
}TypeGuard.Core β Platform-agnostic logic
βββ Abstractions β Interfaces
βββ Handlers β Type-specific handlers
βββ Rules β Validation rules
βββ Builders β Fluent API builders
TypeGuard.Console β Console implementation
βββ ConsoleInput β Reads from Console.ReadLine()
βββ ConsoleOutput β Writes prompts and errors
βββ Guard β Main entry point
TypeGuard.Winforms β WinForms implementation (TextBox, TextBlock)
βββ WinformsInput β Reads from Control.Text
βββ WinformsOutput β Writes prompts and errors
βββ Guard β Main entry point
TypeGuard.Wpf β WPF implementation (TextBox, TextBlock)
βββ WpfInput β Reads from Textbox.Text
βββ WpfOutput β Writes prompts and errors
βββ Guard β Main entry point
TypeGuard.Avalonia β Avalonia implementation (TextBox, TextBlock)
βββ AvaloniaInput β Reads from Textbox.Text
βββ AvaloniaOutput β Writes prompts and errors
βββ Guard β Main entry point
TypeGuard.Maui β MAUI implementation (Entry, Label, Button)
βββ MauiInput β Reads from Entry.Text
βββ MauiOutput β Writes prompts and errors
βββ Guard β Main entry point
TypeGuard.Blazor β Blazor implementation (InputText, DI-injected Guard)
βββ BlazorInput β Reads from InputText.Value
βββ BlazorOutput β Writes prompts and errors
βββ Guard β Main entry point
using TypeGuard.Core.Interfaces;
public class EmailRule : IValidatorRule<string>
{
public bool IsValid(string value) =>
value.Contains('@') && value.Contains('.');
public string ErrorMessage => "Must be a valid email address";
}
string email = await Guard.String("Enter email")
.WithCustomRule(new EmailRule())
.GetAsync();
// Note: Email Rule already exists but this is just an example of how to create your own custom rule.- .NET 9.0 or .NET 10.0
- Windows, macOS, or Linux (certain packages may have OS restrictions)
See LICENCE for details. (MIT Licence)
Contributions are welcome! You can:
- Report bugs or request features via Issues
- Submit pull requests
- Suggest improvements to the API
- Adding new type handlers/builders/rules