ARSoft.WinForms.CustomControls 2.0.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package ARSoft.WinForms.CustomControls --version 2.0.1
                    
NuGet\Install-Package ARSoft.WinForms.CustomControls -Version 2.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ARSoft.WinForms.CustomControls" Version="2.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ARSoft.WinForms.CustomControls" Version="2.0.1" />
                    
Directory.Packages.props
<PackageReference Include="ARSoft.WinForms.CustomControls" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ARSoft.WinForms.CustomControls --version 2.0.1
                    
#r "nuget: ARSoft.WinForms.CustomControls, 2.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ARSoft.WinForms.CustomControls@2.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ARSoft.WinForms.CustomControls&version=2.0.1
                    
Install as a Cake Addin
#tool nuget:?package=ARSoft.WinForms.CustomControls&version=2.0.1
                    
Install as a Cake Tool

ARSoft.WinForms.CustomControls

A comprehensive Windows Forms control library that extends standard WinForms controls with enhanced validation, formatting, and user experience features. Perfect for building robust desktop applications with professional input handling, document validation capabilities, and seamless async operations.

🌟 Features

  • Enhanced Text Controls - Smart text boxes with built-in validation and formatting
  • Document Validation - Support for Brazilian documents (CPF, CNPJ, CEP)
  • Specialized Input Controls - Currency, numeric, email, and integer input handling
  • Improved Labels - Temporary message display with auto-restore functionality
  • Settings Management - Easy import/export of application settings
  • Validation Framework - Consistent validation across all controls
  • Culture Support - Localization-ready with culture-aware formatting
  • Smart Placeholder Handling - Intelligent placeholder text that doesn't interfere with validation
  • ⚑ Async Operations - Built-in loading overlays for async API calls (v2.0.1)

πŸ“¦ Installation

# Via NuGet Package Manager
Install-Package ARSoft.WinForms.CustomControls

# Via .NET CLI
dotnet add package ARSoft.WinForms.CustomControls

πŸ†• What's New in v2.0.1

Revolutionary Features

⚑ Built-in Async Operations with Loading Overlay

  • Automatic loading spinner for async API calls
  • GDI+ drawn spinner - no external files, scales perfectly
  • Smart parent container detection (GroupBox/Panel/Form)
  • Semi-transparent overlay dims parent during operation
  • Integrated error handling with MessageBoxHelper
  • Thread-safe with proper cross-thread handling

🎯 Document Auto-Lookup

  • CEPTextBox - Automatic address lookup from postal code
  • CNPJTextBox - Company information lookup
  • CPFTextBox - Async CPF validation with external services
  • All with visual feedback and error handling

πŸ”§ Extension Methods

  • ExecuteWithLoadingAsync() - Add loading overlay to any control
  • Clean, fluent API for async operations

🎨 Customizable Spinner

  • Adjustable colors, size, and animation speed
  • Professional Material Design-inspired appearance
  • Customizable overlay transparency

v1.2.1 Improvements (Included)

🎯 Smart Placeholder Validation

  • Placeholder text properly ignored during validation
  • IsEmpty property correctly returns true when placeholder is displayed
  • TypedValue properties return appropriate empty values when showing placeholders
  • Required field validation no longer treats placeholder as valid input

πŸš€ Quick Start

Basic Text Input with Validation

using ARSoft.WinForms.CustomControls;

// Create a required text box with validation
var nameTextBox = new ARSTextBox
{
    IsRequired = true,
    RequiredFieldLabel = nameLabel // Label turns red if validation fails
};

// Add placeholder text (properly handled in validation)
nameTextBox.SetPlaceholder("Enter your full name");

// Check validation - placeholder is considered empty
if (nameTextBox.IsValid)
{
    Console.WriteLine($"Valid input: {nameTextBox.Text}");
}

πŸ†• CEP with Auto Address Lookup (v2.0.1)

var cepTextBox = new CEPTextBox
{
    IsRequired = true,
    ApplyMaskOnFocusLeave = true,
    AutoLookupAddress = true, // ⚑ New in v2.0.1
    CepLookupMessage = "Buscando endereΓ§o...",
    OnCepLookupAsync = async (cep) =>
    {
        // Your API call here
        return await YourApiService.GetAddressByCep(cep);
    }
};

cepTextBox.SetPlaceholder("00000-000");

// Handle the result
cepTextBox.AddressFound += (sender, e) =>
{
    streetTextBox.Text = e.Address.Street;
    districtTextBox.Text = e.Address.District;
    cityTextBox.Text = e.Address.City;
    stateTextBox.Text = e.Address.State;
};

// That's it! When user leaves the field:
// 1. Loading overlay appears on parent container
// 2. API call executes
// 3. Address fields auto-populate
// 4. Overlay disappears

πŸ†• CNPJ with Company Lookup (v2.0.1)

var cnpjTextBox = new CNPJTextBox
{
    IsRequired = true,
    ApplyMaskOnFocusLeave = true,
    AutoLookupCompany = true, // ⚑ New in v2.0.1
    CnpjLookupMessage = "Consultando CNPJ na Receita Federal...",
    OnCnpjLookupAsync = async (cnpj) =>
    {
        return await YourApiService.GetCompanyByCnpj(cnpj);
    }
};

cnpjTextBox.SetPlaceholder("00.000.000/0000-00");

cnpjTextBox.CompanyFound += (sender, e) =>
{
    companyNameTextBox.Text = e.Company.Name;
    tradeNameTextBox.Text = e.Company.TradeName;
    statusLabel.Text = e.Company.Status;
};

πŸ†• CPF with Async Validation (v2.0.1)

var cpfTextBox = new CPFTextBox
{
    IsRequired = true,
    AutoValidateCpf = true, // ⚑ New in v2.0.1
    CpfValidationMessage = "Validando CPF...",
    OnCpfValidateAsync = async (cpf) =>
    {
        // Call external validation service
        return await YourApiService.ValidateCpf(cpf);
    }
};

cpfTextBox.SetPlaceholder("000.000.000-00");

cpfTextBox.CpfValidationCompleted += (sender, e) =>
{
    if (e.Success)
    {
        statusLabel.Text = "CPF vΓ‘lido βœ“";
        statusLabel.ForeColor = Color.Green;
    }
    else
    {
        statusLabel.Text = "CPF invΓ‘lido ou nΓ£o encontrado";
        statusLabel.ForeColor = Color.Red;
    }
};

πŸ†• Generic Async Operations (v2.0.1)

var emailTextBox = new EmailTextBox
{
    IsRequired = true,
    ShowLoadingOnAsync = true, // ⚑ New in v2.0.1
    LoadingMessage = "Verificando disponibilidade...",
    OnLeaveAsync = async (email) =>
    {
        var exists = await YourApiService.EmailExists(email);
        if (exists)
        {
            MessageBoxHelper.ShowWarning("Este email jΓ‘ estΓ‘ cadastrado!");
        }
    }
};

emailTextBox.SetPlaceholder("user@example.com");

πŸ†• Extension Method for Any Control (v2.0.1)

// Use extension method for one-off async operations
var customerIdBox = new IntegerTextBox();

await customerIdBox.ExecuteWithLoadingAsync(async () =>
{
    var customer = await YourApiService.GetCustomer(customerIdBox.TypedValue);
    nameTextBox.Text = customer.Name;
    emailTextBox.Text = customer.Email;
}, "Carregando dados do cliente...");

πŸ†• Manual Loading Overlay (v2.0.1)

// For complex scenarios, use AsyncLoadingOverlay directly
using (var loading = AsyncLoadingOverlay.Show(customerGroupBox, "Salvando dados..."))
{
    try
    {
        await YourApiService.SaveCustomer(GetCustomerData());
        MessageBoxHelper.ShowInfo("Cliente salvo com sucesso!");
    }
    catch (Exception ex)
    {
        MessageBoxHelper.ShowError($"Erro ao salvar: {ex.Message}");
    }
}
// Overlay automatically disposed and controls re-enabled

Document Validation Controls

CPF Input
var cpfTextBox = new CPFTextBox
{
    IsRequired = true,
    ApplyMaskOnFocusLeave = true // Automatically formats as XXX.XXX.XXX-XX
};

cpfTextBox.SetPlaceholder("000.000.000-00");

// Returns null if invalid OR if showing placeholder
string cleanCpf = cpfTextBox.TypedValue;
CNPJ Input
var cnpjTextBox = new CNPJTextBox
{
    IsRequired = true,
    ApplyMaskOnFocusLeave = true // Formats as XX.XXX.XXX/XXXX-XX
};

cnpjTextBox.SetPlaceholder("00.000.000/0000-00");

// Validate and get clean value
if (cnpjTextBox.IsValid)
{
    string cleanCnpj = cnpjTextBox.TypedValue; // null if placeholder shown
}
Brazilian Postal Code (CEP)
var cepTextBox = new CEPTextBox
{
    IsRequired = true,
    ApplyMaskOnFocusLeave = true // Formats as XXXXX-XXX
};

cepTextBox.SetPlaceholder("00000-000");

// Returns numbers only, or null if placeholder/invalid
string cleanCep = cepTextBox.TypedValue;

Numeric Input Controls

Currency Input
var priceTextBox = new ARSCurrencyTextBox
{
    MinValue = 0,
    MaxValue = 999999.99,
    Culture = new CultureInfo("pt-BR") // Brazilian currency format
};

priceTextBox.SetPlaceholder("R$ 0,00");

// Set and get currency values
priceTextBox.TypedValue = 1250.75; // Displays as R$ 1.250,75

// Returns double.MinValue if showing placeholder or invalid
double price = priceTextBox.TypedValue;
Integer Input
var quantityTextBox = new IntegerTextBox
{
    MinValue = 1,
    MaxValue = 1000,
    IsRequired = true
};

quantityTextBox.SetPlaceholder("Quantity");

// Returns int.MinValue if showing placeholder or invalid
int quantity = quantityTextBox.TypedValue;
Double/Decimal Input
var weightTextBox = new DoubleTextBox
{
    MinValue = 0.1,
    MaxValue = 999.99
};

weightTextBox.SetPlaceholder("0.00");

// Returns double.MinValue if showing placeholder or invalid
double weight = weightTextBox.TypedValue;

Email Validation

var emailTextBox = new EmailTextBox
{
    IsRequired = true,
    RequiredFieldLabel = emailLabel
};

emailTextBox.SetPlaceholder("user@example.com");

// Email format is automatically validated on focus leave
// Placeholder is properly ignored during validation

Enhanced Labels with Temporary Messages

Standard Label with Auto-Restore
var statusLabel = new ARSLabel
{
    InitialText = "Ready",
    RestoreInitialTextAfterTimeout = true,
    TextTimeout = 3000 // 3 seconds
};

// Show temporary message with optional color
statusLabel.ShowTemporary("Processing...", Color.Blue);
// Automatically reverts to "Ready" after 3 seconds
ToolStrip Label
var toolStripStatus = new ARSToolStripLabel
{
    InitialText = "Ready",
    RestoreInitialTextAfterTimeout = true,
    TextTimeout = 5000
};

toolStripStatus.ShowTemporary("File saved successfully!");
Temporary Visibility Mode
var notificationLabel = new ARSLabel
{
    TemporaryVisibility = true, // Hides after timeout
    TextTimeout = 2000
};

// Label becomes visible, then auto-hides after 2 seconds
notificationLabel.ShowTemporary("Upload complete!", Color.Green);

Password Input with Reveal Feature

var passwordTextBox = new ARSTextBox
{
    UseSystemPasswordChar = true,
    ShowPasswordOnMouseOver = true // Reveals password on hover
};

passwordTextBox.SetPlaceholder("Enter password");

Settings Management

Export Settings
var settingsManager = new SettingsManager();

try
{
    settingsManager.Export(@"C:\backup\app-settings.config");
    MessageBoxHelper.ShowInfo("Settings exported successfully!");
}
catch (Exception ex)
{
    MessageBoxHelper.ShowError($"Failed to export settings: {ex.Message}");
}
Import Settings
var settingsManager = new SettingsManager();

try
{
    settingsManager.Import(
        @"C:\backup\app-settings.config", 
        "MyApp.Properties.Settings"
    );
    MessageBoxHelper.ShowInfo("Settings imported successfully!");
}
catch (Exception ex)
{
    MessageBoxHelper.ShowError($"Failed to import settings: {ex.Message}");
}

πŸ› οΈ Utility Classes

Message Box Helpers

// Simplified message boxes
MessageBoxHelper.ShowError("Something went wrong!");
MessageBoxHelper.ShowWarning("Please check your input");
MessageBoxHelper.ShowInfo("Operation completed successfully");

String Utilities

// Convert to title case
string title = Util.ToTitleCase("hello world"); // "Hello World"

// Capitalize first letter only
string name = Util.FirstLetterToUpper("john"); // "John"

Document Validation

// Validate documents without UI controls
bool isValidCpf = DocumentValidations.IsCPF("123.456.789-00");
bool isValidCnpj = DocumentValidations.IsCNPJ("12.345.678/0001-95");
bool isValidCep = DocumentValidations.IsCep("12345-678");
bool isValidEmail = DocumentValidations.IsEmail("user@example.com");

🎨 Advanced Features

πŸ†• Customizing Loading Overlay (v2.0.1)

var overlay = AsyncLoadingOverlay.Show(customerGroupBox, "Loading...");
overlay.SpinnerColor = Color.FromArgb(66, 165, 245); // Material Blue
overlay.SpinnerSize = 64;
overlay.SpinnerThickness = 5;
overlay.OverlayColor = Color.FromArgb(180, 0, 0, 0); // More opaque
overlay.AnimationSpeed = 30; // Faster animation

πŸ†• Form-Level Async Save (v2.0.1)

private async void SaveButton_Click(object sender, EventArgs e)
{
    if (!ValidateForm())
    {
        MessageBoxHelper.ShowWarning("Corrija os campos invΓ‘lidos.");
        return;
    }

    try
    {
        // Show loading on entire form
        await AsyncLoadingOverlay.ExecuteAsync(
            this, // The form itself
            async () =>
            {
                await YourApiService.SaveData(CollectFormData());
            },
            "Salvando dados..."
        );

        MessageBoxHelper.ShowInfo("Dados salvos com sucesso!");
        this.Close();
    }
    catch
    {
        // Error already shown by overlay
    }
}

Form-Wide Validation

private bool ValidateForm()
{
    var controls = this.GetAllControls<ICustomControlsARS>();
    
    // All controls properly handle placeholder validation
    return controls.All(control => control.IsValid);
}

private void SaveButton_Click(object sender, EventArgs e)
{
    if (!ValidateForm())
    {
        MessageBoxHelper.ShowWarning("Please fix validation errors before saving.");
        return;
    }
    
    // Proceed with save operation
    SaveData();
}

Working with TypedValue Properties

// TypedValue correctly handles placeholder state
private void ProcessForm()
{
    // Returns null if placeholder is shown
    string cpf = cpfTextBox.TypedValue;
    
    // Returns double.MinValue if placeholder is shown
    double price = priceTextBox.TypedValue;
    
    // Returns int.MinValue if placeholder is shown
    int quantity = quantityTextBox.TypedValue;
    
    // Check for valid input before processing
    if (cpf != null && price > 0 && quantity > 0)
    {
        // Process valid data
        ProcessOrder(cpf, price, quantity);
    }
}

Cultural Formatting

var currencyBox = new ARSCurrencyTextBox();

// Brazilian format
currencyBox.Culture = new CultureInfo("pt-BR");
currencyBox.SetPlaceholder("R$ 0,00");
currencyBox.TypedValue = 1000; // Displays: R$ 1.000,00

// US format
currencyBox.Culture = new CultureInfo("en-US");
currencyBox.SetPlaceholder("$0.00");
currencyBox.TypedValue = 1000; // Displays: $1,000.00

πŸ”§ Interface Overview

Interface Purpose
ICustomControlsARS Base interface for validation and required field handling
ICustomLabelARS Interface for labels with temporary message capabilities
INumericRangeControlARS Interface for controls with min/max value constraints
IDocumentField Interface for document input controls with masking

πŸ“‹ Control Summary

Control Purpose Key Features
ARSTextBox Enhanced text input Validation, placeholders, password reveal, async operations
CPFTextBox Brazilian CPF input Auto-masking, validation, clean value extraction, async validation
CNPJTextBox Brazilian CNPJ input Auto-masking, validation, clean value extraction, company lookup
CEPTextBox Brazilian postal code Auto-masking, validation, automatic address lookup
EmailTextBox Email address input Format validation, error highlighting, async availability check
DoubleTextBox Decimal number input Range validation, culture-aware parsing
IntegerTextBox Integer input Range validation, input restriction
ARSCurrencyTextBox Currency input Culture-aware formatting, range validation
ARSLabel Enhanced label Temporary messages, auto-restore, color changes
ARSToolStripLabel Enhanced ToolStrip label Temporary messages for status bars
AsyncLoadingOverlay ⚑ Loading overlay (v2.0.1) Async operation visual feedback with spinner

πŸ’‘ Best Practices

πŸ†• Async Operations (v2.0.1)

// βœ… DO: Use auto-lookup for common scenarios
cepTextBox.AutoLookupAddress = true;
cepTextBox.OnCepLookupAsync = cep => ApiService.GetAddress(cep);

// βœ… DO: Use extension methods for one-off operations
await control.ExecuteWithLoadingAsync(async () => {
    await DoSomethingAsync();
}, "Please wait...");

// βœ… DO: Let the library handle errors
// Timeouts and errors automatically show MessageBox

// ❌ DON'T: Wrap in try-catch unless you need custom error handling
// The AsyncLoadingOverlay already handles this

Placeholder Usage

// βœ… DO: Use descriptive placeholders
emailTextBox.SetPlaceholder("usuario@exemplo.com.br");
cpfTextBox.SetPlaceholder("000.000.000-00");

// ❌ DON'T: Rely on placeholder for validation
// The control now properly handles this, but placeholders should guide, not validate

Validation Patterns

// βœ… DO: Check IsEmpty before processing
if (!customerNameBox.IsEmpty)
{
    string name = customerNameBox.Text;
    ProcessCustomer(name);
}

// βœ… DO: Use TypedValue for numeric/document controls
if (priceBox.TypedValue != double.MinValue)
{
    decimal price = (decimal)priceBox.TypedValue;
    CalculateTotal(price);
}

// βœ… DO: Check for null on document controls
string cpf = cpfBox.TypedValue;
if (cpf != null)
{
    ValidateCustomer(cpf);
}

Required Field Handling

// βœ… DO: Assign labels to show validation state
var nameBox = new ARSTextBox
{
    IsRequired = true,
    RequiredFieldLabel = nameLabel
};

// The label automatically turns red when validation fails
// Placeholder is properly considered as "empty" for required fields

🀝 Contributing

Contributions are welcome! Please feel free to submit pull requests or open issues for bugs and feature requests.

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ†˜ Support

If you encounter any issues or have questions, please:

  1. Check the documentation and examples above
  2. Search existing issues on GitHub
  3. Create a new issue with detailed information about your problem

πŸ“ Changelog

v2.0.1 (Current) - Major Release

  • ⚑ Async Operations: Built-in loading overlay for async API calls
  • 🎨 GDI+ Spinner: Professional animated spinner with no external dependencies
  • πŸ” Auto-Lookup: CEP address lookup, CNPJ company lookup, CPF validation
  • 🎯 Smart Overlay: Automatic parent container detection and dimming
  • 🧩 Extension Methods: ExecuteWithLoadingAsync() for any control
  • 🎨 Customizable: Spinner color, size, speed, overlay transparency
  • πŸ”§ Thread-Safe: Proper cross-thread UI handling
  • πŸ“¦ Event-Driven: Events for async operation completion
  • πŸ›‘οΈ Error Handling: Integrated with MessageBoxHelper for timeouts and errors

v1.2.1

  • ✨ Smart placeholder validation - placeholders no longer interfere with validation
  • πŸ› Fixed IsEmpty property to correctly identify placeholder state
  • πŸ› Fixed TypedValue properties to return appropriate empty values when placeholder is shown
  • πŸ› Improved DocumentTextBox validation to skip format checking on placeholders
  • πŸ› Enhanced ARSCurrencyTextBox to skip formatting when placeholder is displayed
  • πŸ”§ All numeric controls now check for placeholder before parsing values

v1.0.0

  • Initial public release
  • Full suite of enhanced WinForms controls
  • Document validation for Brazilian documents
  • Settings management utilities
  • Comprehensive validation framework

Made for the WinForms enthusiastic community

Product Compatible and additional computed target framework versions.
.NET net8.0-windows7.0 is compatible.  net9.0-windows was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0-windows7.0

    • No dependencies.

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
3.0.3 107 5/9/2026
2.0.1 184 10/31/2025
1.2.1 232 9/1/2025