@using System.Globalization @code { /// /// Actual bind value /// [Parameter] public decimal Value { get; set; } /// /// Event for value changed /// [Parameter] public EventCallback ValueChanged { get; set; } /// /// CssClass for input component /// [Parameter] public string CssClass { get; set; } = string.Empty; /// /// Number of decimal places to display in percentage format. /// Example: Decimals=2 → "15.00%". /// [Parameter] public int Decimals { get; set; } = 2; /// /// If true, parsing is always done with InvariantCulture. /// If false (default), try CurrentCulture first, then InvariantCulture. /// [Parameter] public bool ForceInvariantParsing { get; set; } = false; /// /// Percentage format string /// private string DisplayValue => Value.ToString($"P{Decimals}"); /// /// Change value event /// private async Task OnChange(ChangeEventArgs e) { var input = e.Value?.ToString()?.Trim(); if (string.IsNullOrWhiteSpace(input)) { Value = 0; await ValueChanged.InvokeAsync(0); return; } // Remove % if present var cleaned = input.Replace("%", "").Trim(); decimal parsed; bool success; if (ForceInvariantParsing) { // Option 3: Always invariant success = decimal.TryParse(cleaned, NumberStyles.Any, CultureInfo.InvariantCulture, out parsed); } else { // Option 2: Try current culture, then invariant success = decimal.TryParse(cleaned, NumberStyles.Any, CultureInfo.CurrentCulture, out parsed) || decimal.TryParse(cleaned, NumberStyles.Any, CultureInfo.InvariantCulture, out parsed); } if (success) { decimal result = parsed <= 1 ? parsed : parsed / 100; Value = result; await ValueChanged.InvokeAsync(result); } else { // Invalid input → reset to 0 Value = 0; await ValueChanged.InvokeAsync(0); } } }