Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
913b5a3
Implement `[BindableProperty]` for `CharactersValidationBehavior`
TheCodeTraveler Dec 5, 2025
5299297
Update CharactersValidationBehavior.shared.cs
TheCodeTraveler Dec 5, 2025
356ce9e
Add `[BindableProperty]` to `[MultiValidationBehavior]`
TheCodeTraveler Dec 5, 2025
2800fb6
Add Edge Cases for Float + Double
TheCodeTraveler Dec 5, 2025
50a1053
Add `[BindableProperty]` to `NumericValidationBehavior`
TheCodeTraveler Dec 5, 2025
e497442
Add Support for `float`
TheCodeTraveler Dec 5, 2025
4acecc3
Remove support for Epsilon
TheCodeTraveler Dec 5, 2025
f1fa87d
Add `[BindableProperty]` to `RequiredStringValidationBehavior`
TheCodeTraveler Dec 5, 2025
ba0be8d
Add `[BindableProperty]` to `TextValidationBehavior`
TheCodeTraveler Dec 5, 2025
0799631
Merge branch 'main' into Implement-`-BindableProperty]`-for-Validatio…
TheCodeTraveler Dec 12, 2025
2e18a6f
Convert to partial property initializers
TheCodeTraveler Dec 12, 2025
936875a
Convert to partial property initializers
TheCodeTraveler Dec 12, 2025
430b6b4
Implement `[BindableProperty]`
TheCodeTraveler Dec 12, 2025
a8c0eca
Remove unneeded tests
TheCodeTraveler Dec 12, 2025
76d17f6
Revert Attached Bindable Property
TheCodeTraveler Dec 12, 2025
7607ebc
Update MultiValidationBehavior.shared.cs
TheCodeTraveler Dec 12, 2025
e40e679
Merge branch 'main' into Implement-`-BindableProperty]`-for-Validatio…
TheCodeTraveler Jan 10, 2026
5ce8b99
Remove duplicate files
TheCodeTraveler Jan 10, 2026
6fd1051
`dotnet format`
TheCodeTraveler Jan 10, 2026
adb0bff
Update formatting
TheCodeTraveler Jan 10, 2026
089b2ed
Merge branch 'main' into Implement-`-BindableProperty]`-for-Validatio…
TheCodeTraveler Jan 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace CommunityToolkit.Maui.Core;

static class MultiValidationBehaviorDefaults
{
public static List<object?>? Errors { get; } = null;
public static List<object?>? Error { get; } = null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace CommunityToolkit.Maui.Core;

static class NumericValidationBehaviorDefaults
{
public const double MinimumValue = double.NegativeInfinity;
public const double MaximumValue = double.PositiveInfinity;
public const int MinimumDecimalPlaces = 0;
public const int MaximumDecimalPlaces = int.MaxValue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace CommunityToolkit.Maui.Core;

static class RequiredStringValidationBehaviorDefaults
{
public const string? RequiredString = null;
public const bool ExactMatch = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace CommunityToolkit.Maui.Core;

static class UriValidationBehaviorDefaults
{
public static UriKind UriKind { get; } = UriKind.RelativeOrAbsolute;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CommunityToolkit.Maui.Behaviors;
using CommunityToolkit.Maui.Core;
using Nito.AsyncEx;
using Xunit;

Expand Down Expand Up @@ -125,4 +126,14 @@ public void EnsureObjectDisposedExceptionThrownWhenDisposedBehaviorAttachedToVis
});
});
}

[Fact]
public void VerifyDefaults()
{
var charactersValidationBehavior = new CharactersValidationBehavior();

Assert.Equal(CharactersValidationBehaviorDefaults.CharacterType, charactersValidationBehavior.CharacterType);
Assert.Equal(CharactersValidationBehaviorDefaults.MaximumCharacterTypeCount, charactersValidationBehavior.MaximumCharacterTypeCount);
Assert.Equal(CharactersValidationBehaviorDefaults.MinimumCharacterTypeCount, charactersValidationBehavior.MinimumCharacterTypeCount);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CommunityToolkit.Maui.Behaviors;
using CommunityToolkit.Maui.Core;
using Xunit;

namespace CommunityToolkit.Maui.UnitTests.Behaviors;
Expand Down Expand Up @@ -119,4 +120,15 @@ public async Task ForceValidateCancellationTokenCanceled()
// Assert
await Assert.ThrowsAsync<OperationCanceledException>(async () => await multiBehavior.ForceValidate(cts.Token));
}

[Fact]
public void VerifyDefaults()
{
// Arrange
var multiValidationBehavior = new MultiValidationBehavior();

// Act // Assert
Assert.Equal(MultiValidationBehaviorDefaults.Errors, multiValidationBehavior.Errors);
Assert.Equal(MultiValidationBehaviorDefaults.Error, MultiValidationBehavior.GetError(multiValidationBehavior));
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Globalization;
using CommunityToolkit.Maui.Behaviors;
using CommunityToolkit.Maui.Core;
using FluentAssertions;
using Xunit;

Expand Down Expand Up @@ -163,4 +164,17 @@ await Assert.ThrowsAsync<OperationCanceledException>(async () =>
await behavior.ForceValidate(cts.Token);
});
}

[Fact]
public void VerifyDefaults()
{
// Arrange
var numericValidationBehavior = new NumericValidationBehavior();

// Act Assert
Assert.Equal(NumericValidationBehaviorDefaults.MaximumDecimalPlaces, numericValidationBehavior.MaximumDecimalPlaces);
Assert.Equal(NumericValidationBehaviorDefaults.MaximumValue, numericValidationBehavior.MaximumValue);
Assert.Equal(NumericValidationBehaviorDefaults.MinimumDecimalPlaces, numericValidationBehavior.MinimumDecimalPlaces);
Assert.Equal(NumericValidationBehaviorDefaults.MinimumValue, numericValidationBehavior.MinimumValue);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Globalization;
using CommunityToolkit.Maui.Behaviors;
using CommunityToolkit.Maui.Core;
using Xunit;

namespace CommunityToolkit.Maui.UnitTests.Behaviors;
Expand Down Expand Up @@ -157,4 +158,15 @@ public void IsValidFalseWhenEnterDifferentText_Test()
// Assert
Assert.False(confirmPasswordBehavior.IsValid);
}

[Fact]
public void VerifyDefaults()
{
// Arrange
var requiredStringValidationBehavior = new RequiredStringValidationBehavior();

// Act Assert
Assert.Equal(RequiredStringValidationBehaviorDefaults.ExactMatch, requiredStringValidationBehavior.ExactMatch);
Assert.Equal(RequiredStringValidationBehaviorDefaults.RequiredString, requiredStringValidationBehavior.RequiredString);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,18 @@ await Assert.ThrowsAsync<OperationCanceledException>(async () =>
await behavior.ForceValidate(cts.Token);
});
}

[Fact]
public void VerifyDefaults()
{
// Arrange
var textValidationBehavior = new TextValidationBehavior();

// Act Assert
Assert.Equal(TextValidationBehaviorDefaults.DecorationFlags, textValidationBehavior.DecorationFlags);
Assert.Equal(TextValidationBehaviorDefaults.MaximumLength, textValidationBehavior.MaximumLength);
Assert.Equal(TextValidationBehaviorDefaults.MinimumLength, textValidationBehavior.MinimumLength);
Assert.Equal(TextValidationBehaviorDefaults.RegexOptions, textValidationBehavior.RegexOptions);
Assert.Equal(TextValidationBehaviorDefaults.RegexPattern, textValidationBehavior.RegexPattern);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CommunityToolkit.Maui.Behaviors;
using CommunityToolkit.Maui.Core;
using Xunit;

namespace CommunityToolkit.Maui.UnitTests.Behaviors;
Expand Down Expand Up @@ -75,4 +76,14 @@ await Assert.ThrowsAsync<OperationCanceledException>(async () =>
await behavior.ForceValidate(cts.Token);
});
}

[Fact]
public void VerifyDefaults()
{
// Arrange
var behavior = new UriValidationBehavior();

// Act Assert
Assert.Equal(UriValidationBehaviorDefaults.UriKind, behavior.UriKind);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,24 @@ public void TestRemoveValidationBindingWithoutBindingContext()

Assert.Empty(view.Behaviors);
}

[Fact]
public void VerifyDefaults()
{
// Arrange
var behavior = new MockValidationBehavior();

// Act Assert
Assert.Equal(ValidationBehaviorDefaults.IsNotValid, behavior.IsNotValid);
Assert.Equal(ValidationBehaviorDefaults.IsValid, behavior.IsValid);
Assert.Equal(ValidationBehaviorDefaults.IsRunning, behavior.IsRunning);
Assert.Equal(ValidationBehaviorDefaults.ValidStyle, behavior.ValidStyle);
Assert.Equal(ValidationBehaviorDefaults.InvalidStyle, behavior.InvalidStyle);
Assert.Equal(ValidationBehaviorDefaults.Value, behavior.Value);
Assert.Equal(ValidationBehaviorDefaults.ValuePropertyName, behavior.ValuePropertyName);
Assert.Equal(ValidationBehaviorDefaults.Flags, behavior.Flags);

}
}

public class StyleSetterComparer(ITestOutputHelper testOutputHelper) : IEqualityComparer<Setter>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
namespace CommunityToolkit.Maui.Behaviors;

/// <summary>The allowed character types used to determine if a value is valid in the <see cref="CharactersValidationBehavior"/>. Since this is a flag, multiple flags cane be combined.</summary>
Expand Down Expand Up @@ -47,25 +48,7 @@ public enum CharacterType
[RequiresUnreferencedCode($"{nameof(CharactersValidationBehavior)} is not trim safe because it uses bindings with string paths.")]
public partial class CharactersValidationBehavior : TextValidationBehavior
{
List<Predicate<char>> characterPredicates = Enumerable.Empty<Predicate<char>>().ToList();

/// <summary>
/// Backing BindableProperty for the <see cref="CharacterType"/> property.
/// </summary>
public static readonly BindableProperty CharacterTypeProperty =
BindableProperty.Create(nameof(CharacterType), typeof(CharacterType), typeof(CharactersValidationBehavior), CharacterType.Any, propertyChanged: OnCharacterTypePropertyChanged);

/// <summary>
/// Backing BindableProperty for the <see cref="MinimumCharacterTypeCount"/> property.
/// </summary>
public static readonly BindableProperty MinimumCharacterTypeCountProperty =
BindableProperty.Create(nameof(MinimumCharacterTypeCount), typeof(int), typeof(CharactersValidationBehavior), 0, propertyChanged: OnValidationPropertyChanged);

/// <summary>
/// Backing BindableProperty for the <see cref="MaximumCharacterTypeCount"/> property.
/// </summary>
public static readonly BindableProperty MaximumCharacterTypeCountProperty =
BindableProperty.Create(nameof(MaximumCharacterTypeCount), typeof(int), typeof(CharactersValidationBehavior), int.MaxValue, propertyChanged: OnValidationPropertyChanged);
List<Predicate<char>> characterPredicates = [];

/// <summary>
/// Constructor for this behavior
Expand All @@ -75,29 +58,20 @@ public partial class CharactersValidationBehavior : TextValidationBehavior
/// <summary>
/// Provides an enumerated value to use to set how to handle comparisons. This is a bindable property.
/// </summary>
public CharacterType CharacterType
{
get => (CharacterType)GetValue(CharacterTypeProperty);
set => SetValue(CharacterTypeProperty, value);
}
[BindableProperty(PropertyChangedMethodName = nameof(OnCharacterTypePropertyChanged))]
public partial CharacterType CharacterType { get; set; } = CharactersValidationBehaviorDefaults.CharacterType;

/// <summary>
/// The minimum number of <see cref="CharacterType"/> required. This is a bindable property.
/// </summary>
public int MinimumCharacterTypeCount
{
get => (int)GetValue(MinimumCharacterTypeCountProperty);
set => SetValue(MinimumCharacterTypeCountProperty, value);
}
[BindableProperty(PropertyChangedMethodName = nameof(OnValidationPropertyChanged))]
public partial int MinimumCharacterTypeCount { get; set; } = CharactersValidationBehaviorDefaults.MinimumCharacterTypeCount;

/// <summary>
/// The maximum number of <see cref="CharacterType"/> allowed. This is a bindable property.
/// </summary>
public int MaximumCharacterTypeCount
{
get => (int)GetValue(MaximumCharacterTypeCountProperty);
set => SetValue(MaximumCharacterTypeCountProperty, value);
}
[BindableProperty(PropertyChangedMethodName = nameof(OnValidationPropertyChanged))]
public partial int MaximumCharacterTypeCount { get; set; } = CharactersValidationBehaviorDefaults.MaximumCharacterTypeCount;

/// <inheritdoc/>
protected override async ValueTask<bool> ValidateAsync(string? value, CancellationToken token)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using CommunityToolkit.Maui.Core;

namespace CommunityToolkit.Maui.Behaviors;

Expand All @@ -11,12 +12,6 @@ namespace CommunityToolkit.Maui.Behaviors;
[ContentProperty(nameof(Children))]
public partial class MultiValidationBehavior : ValidationBehavior
{
/// <summary>
/// Backing BindableProperty for the <see cref="Errors"/> property.
/// </summary>
public static readonly BindableProperty ErrorsProperty =
BindableProperty.Create(nameof(Errors), typeof(List<object?>), typeof(MultiValidationBehavior), null, BindingMode.OneWayToSource);

/// <summary>
/// BindableProperty for getting the error.
/// </summary>
Expand All @@ -33,11 +28,8 @@ public partial class MultiValidationBehavior : ValidationBehavior
/// <summary>
/// Holds the errors from all the nested invalid validators in <see cref="Children"/>. This is a bindable property.
/// </summary>
public List<object?>? Errors
{
get => (List<object?>?)GetValue(ErrorsProperty);
set => SetValue(ErrorsProperty, value);
}
[BindableProperty(DefaultBindingMode = BindingMode.OneWayToSource)]
public partial List<object?>? Errors { get; set; } = MultiValidationBehaviorDefaults.Errors;

/// <summary>
/// Method to extract the error from the attached property for a child behavior in <see cref="Children"/>.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using CommunityToolkit.Maui.Core;

namespace CommunityToolkit.Maui.Behaviors;

/// <summary>
Expand All @@ -9,65 +11,29 @@ namespace CommunityToolkit.Maui.Behaviors;
[RequiresUnreferencedCode($"{nameof(NumericValidationBehavior)} is not trim safe because it uses bindings with string paths.")]
public partial 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);
}
[BindableProperty(PropertyChangedMethodName = nameof(OnValidationPropertyChanged))]
public partial double MinimumValue { get; set; } = NumericValidationBehaviorDefaults.MinimumValue;

/// <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);
}
[BindableProperty(PropertyChangedMethodName = nameof(OnValidationPropertyChanged))]
public partial double MaximumValue { get; set; } = NumericValidationBehaviorDefaults.MaximumValue;

/// <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);
}
[BindableProperty(PropertyChangedMethodName = nameof(OnValidationPropertyChanged))]
public partial int MinimumDecimalPlaces { get; set; } = NumericValidationBehaviorDefaults.MinimumDecimalPlaces;

/// <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);
}
[BindableProperty(PropertyChangedMethodName = nameof(OnValidationPropertyChanged))]
public partial int MaximumDecimalPlaces { get; set; } = NumericValidationBehaviorDefaults.MaximumDecimalPlaces;

/// <inheritdoc/>
protected override string? Decorate(string? value)
Expand Down
Loading
Loading