-
Notifications
You must be signed in to change notification settings - Fork 340
/
NumericValidationBehavior.shared.cs
101 lines (85 loc) · 3.92 KB
/
NumericValidationBehavior.shared.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
using System.Globalization;
namespace CommunityToolkit.Maui.Behaviors;
/// <summary>
/// The <see cref="NumericValidationBehavior"/> is a behavior that allows the user to determine if text input is a valid numeric value. For example, an <see cref="Entry"/> control can be styled differently depending on whether a valid or an invalid numeric input is provided. Additional properties handling validation are inherited from <see cref="ValidationBehavior"/>.
/// </summary>
public class NumericValidationBehavior : ValidationBehavior<string>
{
/// <summary>
/// Backing BindableProperty for the <see cref="MinimumValue"/> property.
/// </summary>
public static readonly BindableProperty MinimumValueProperty =
BindableProperty.Create(nameof(MinimumValue), typeof(double), typeof(NumericValidationBehavior), double.NegativeInfinity, propertyChanged: OnValidationPropertyChanged);
/// <summary>
/// Backing BindableProperty for the <see cref="MaximumValue"/> property.
/// </summary>
public static readonly BindableProperty MaximumValueProperty =
BindableProperty.Create(nameof(MaximumValue), typeof(double), typeof(NumericValidationBehavior), double.PositiveInfinity, propertyChanged: OnValidationPropertyChanged);
/// <summary>
/// Backing BindableProperty for the <see cref="MinimumDecimalPlaces"/> property.
/// </summary>
public static readonly BindableProperty MinimumDecimalPlacesProperty =
BindableProperty.Create(nameof(MinimumDecimalPlaces), typeof(int), typeof(NumericValidationBehavior), 0, propertyChanged: OnValidationPropertyChanged);
/// <summary>
/// Backing BindableProperty for the <see cref="MaximumDecimalPlaces"/> property.
/// </summary>
public static readonly BindableProperty MaximumDecimalPlacesProperty =
BindableProperty.Create(nameof(MaximumDecimalPlaces), typeof(int), typeof(NumericValidationBehavior), int.MaxValue, propertyChanged: OnValidationPropertyChanged);
/// <summary>
/// The minimum numeric value that will be allowed. This is a bindable property.
/// </summary>
public double MinimumValue
{
get => (double)GetValue(MinimumValueProperty);
set => SetValue(MinimumValueProperty, value);
}
/// <summary>
/// The maximum numeric value that will be allowed. This is a bindable property.
/// </summary>
public double MaximumValue
{
get => (double)GetValue(MaximumValueProperty);
set => SetValue(MaximumValueProperty, value);
}
/// <summary>
/// The minimum number of decimal places that will be allowed. This is a bindable property.
/// </summary>
public int MinimumDecimalPlaces
{
get => (int)GetValue(MinimumDecimalPlacesProperty);
set => SetValue(MinimumDecimalPlacesProperty, value);
}
/// <summary>
/// The maximum number of decimal places that will be allowed. This is a bindable property.
/// </summary>
public int MaximumDecimalPlaces
{
get => (int)GetValue(MaximumDecimalPlacesProperty);
set => SetValue(MaximumDecimalPlacesProperty, value);
}
/// <inheritdoc/>
protected override string? Decorate(string? value)
=> base.Decorate(value)?.Trim();
/// <inheritdoc/>
protected override ValueTask<bool> ValidateAsync(string? value, CancellationToken token)
{
ArgumentNullException.ThrowIfNull(value);
if (!(double.TryParse(value, out var numeric)
&& numeric >= MinimumValue
&& numeric <= MaximumValue))
{
return new ValueTask<bool>(false);
}
var decimalDelimiterIndex = value.IndexOf(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
var hasDecimalDelimiter = decimalDelimiterIndex >= 0;
// If MaximumDecimalPlaces equals zero, ".5" or "14." should be considered as invalid inputs.
if (hasDecimalDelimiter && MaximumDecimalPlaces == 0)
{
return new ValueTask<bool>(false);
}
var decimalPlaces = hasDecimalDelimiter
? value.Substring(decimalDelimiterIndex + 1, value.Length - decimalDelimiterIndex - 1).Length
: 0;
return new ValueTask<bool>(decimalPlaces >= MinimumDecimalPlaces && decimalPlaces <= MaximumDecimalPlaces);
}
}