ARSoft.WinForms.CustomControls
2.0.1
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
<PackageReference Include="ARSoft.WinForms.CustomControls" Version="2.0.1" />
<PackageVersion Include="ARSoft.WinForms.CustomControls" Version="2.0.1" />
<PackageReference Include="ARSoft.WinForms.CustomControls" />
paket add ARSoft.WinForms.CustomControls --version 2.0.1
#r "nuget: ARSoft.WinForms.CustomControls, 2.0.1"
#:package ARSoft.WinForms.CustomControls@2.0.1
#addin nuget:?package=ARSoft.WinForms.CustomControls&version=2.0.1
#tool nuget:?package=ARSoft.WinForms.CustomControls&version=2.0.1
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 codeCNPJTextBox- Company information lookupCPFTextBox- 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
IsEmptyproperty correctly returnstruewhen placeholder is displayedTypedValueproperties 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:
- Check the documentation and examples above
- Search existing issues on GitHub
- 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
IsEmptyproperty to correctly identify placeholder state - π Fixed
TypedValueproperties to return appropriate empty values when placeholder is shown - π Improved
DocumentTextBoxvalidation to skip format checking on placeholders - π Enhanced
ARSCurrencyTextBoxto 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 | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0-windows7.0 is compatible. net9.0-windows was computed. net10.0-windows was computed. |
-
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.