Skip to content

Commit fbf9d86

Browse files
committed
Add culture-aware decimal input cleaning to formatter
Implemented CleanInput in DecimalFormatter to normalize and sanitize decimal input strings, handling group and decimal separators per culture and limiting fractional digits. DecimalEntryBehavior now delegates input cleaning to the formatter for consistent, culture-aware normalization.
1 parent 0176316 commit fbf9d86

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

src/ISynergy.Framework.Core/Formatters/DecimalFormatter.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,63 @@ public override string FormatValue(decimal value)
2121
return value.ToString($"N{_decimalPlaces}", _culture);
2222
}
2323

24+
/// <summary>
25+
/// Cleans and normalizes decimal input, preserving the fractional part up to DecimalPlaces.
26+
/// </summary>
27+
public override string CleanInput(string input)
28+
{
29+
if (string.IsNullOrWhiteSpace(input))
30+
return string.Empty;
31+
32+
var decimalSeparator = _culture.NumberFormat.NumberDecimalSeparator;
33+
var groupSeparator = _culture.NumberFormat.NumberGroupSeparator;
34+
35+
// Remove group separators
36+
var cleaned = input.Replace(groupSeparator, string.Empty).Trim();
37+
38+
// Normalize alternative decimal separators to culture-specific separator
39+
var alternativeSeparators = new[] { ".", ",", "·", "٫" }
40+
.Where(s => s != decimalSeparator && s != groupSeparator)
41+
.ToArray();
42+
43+
foreach (var separator in alternativeSeparators)
44+
{
45+
cleaned = cleaned.Replace(separator, decimalSeparator);
46+
}
47+
48+
// Split on the decimal separator
49+
if (cleaned.Contains(decimalSeparator))
50+
{
51+
var parts = cleaned.Split(new[] { decimalSeparator }, StringSplitOptions.None);
52+
53+
if (parts.Length > 2)
54+
{
55+
// Multiple decimal separators - take first two parts only
56+
parts = new[] { parts[0], parts[1] };
57+
}
58+
59+
// Preserve integer part and fractional part (up to DecimalPlaces)
60+
var integerPart = parts[0];
61+
var fractionalPart = parts.Length > 1 ? parts[1] : string.Empty;
62+
63+
// Limit fractional part to DecimalPlaces
64+
if (fractionalPart.Length > _decimalPlaces)
65+
{
66+
fractionalPart = fractionalPart.Substring(0, _decimalPlaces);
67+
}
68+
69+
// Rejoin with culture-specific decimal separator
70+
if (!string.IsNullOrEmpty(fractionalPart))
71+
{
72+
return integerPart + decimalSeparator + fractionalPart;
73+
}
74+
75+
return integerPart;
76+
}
77+
78+
return cleaned;
79+
}
80+
2481
public bool HasValidDecimalPlaces(decimal value)
2582
{
2683
var decimalPlaces = BitConverter.GetBytes(decimal.GetBits(value)[3])[2];

src/ISynergy.Framework.UI.Maui/Behaviors/DecimalEntryBehavior.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ protected override string FormatValue(decimal value)
5959
return _formatter.FormatValue(rounded);
6060
}
6161

62+
protected override string CleanInput(string input)
63+
{
64+
if (string.IsNullOrWhiteSpace(input))
65+
return string.Empty;
66+
67+
_formatter ??= new DecimalFormatter(Culture, DecimalPlaces);
68+
69+
// Use the formatter's CleanInput which preserves the fractional part
70+
return _formatter.CleanInput(input);
71+
}
72+
6273
private void UpdateFormatter()
6374
{
6475
_formatter = new DecimalFormatter(Culture, DecimalPlaces);

0 commit comments

Comments
 (0)