diff --git a/src/CommunityToolkit.Maui.Core/Primitives/Defaults/AvatarViewDefaults.shared.cs b/src/CommunityToolkit.Maui.Core/Primitives/Defaults/AvatarViewDefaults.shared.cs
index 9c783bbc5d..ba9ed825be 100644
--- a/src/CommunityToolkit.Maui.Core/Primitives/Defaults/AvatarViewDefaults.shared.cs
+++ b/src/CommunityToolkit.Maui.Core/Primitives/Defaults/AvatarViewDefaults.shared.cs
@@ -6,23 +6,23 @@ namespace CommunityToolkit.Maui.Core;
static class AvatarViewDefaults
{
/// Default avatar border width.
- public const double DefaultBorderWidth = 1;
+ public const double BorderWidth = 1;
/// Default height request.
- public const double DefaultHeightRequest = 48;
+ public const double HeightRequest = 48;
/// Default avatar text.
- public const string DefaultText = "?";
+ public const string Text = "?";
/// Default width request.
- public const double DefaultWidthRequest = 48;
+ public const double WidthRequest = 48;
/// default avatar border colour.
- public static Color DefaultBorderColor { get; } = Colors.White;
+ public static Color BorderColor { get; } = Colors.White;
/// Default corner radius.
- public static CornerRadius DefaultCornerRadius { get; } = new(24, 24, 24, 24);
+ public static CornerRadius CornerRadius { get; } = new(24, 24, 24, 24);
/// Default padding.
- public static Thickness DefaultPadding { get; } = new(1);
+ public static Thickness Padding { get; } = new(1);
}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/CommonUsageTests.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/CommonUsageTests.cs
index d12688ef2f..593bcabf7f 100644
--- a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/CommonUsageTests.cs
+++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/CommonUsageTests.cs
@@ -652,4 +652,77 @@ public partial class {{defaultTestClassName}}
await VerifySourceGeneratorAsync(source, expectedGenerated);
}
+
+
+ [Fact]
+ public async Task GenerateBindableProperty_WithInitializer_GeneratesCorrectCode()
+ {
+ const string source =
+ /* language=C#-test */
+ //lang=csharp
+ $$"""
+ using CommunityToolkit.Maui;
+ using Microsoft.Maui.Controls;
+ using System;
+
+ namespace {{defaultTestNamespace}};
+
+ public partial class {{defaultTestClassName}} : View
+ {
+ [BindablePropertyAttribute]
+ public partial string Text { get; set; } = "Initial Value";
+
+ [BindablePropertyAttribute]
+ public partial TimeSpan CustomDuration { get; set; } = TimeSpan.FromSeconds(30);
+ }
+ """;
+
+ const string expectedGenerated =
+ /* language=C#-test */
+ //lang=csharp
+ $$"""
+ //
+ // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
+ #pragma warning disable
+ #nullable enable
+ namespace {{defaultTestNamespace}};
+ public partial class {{defaultTestClassName}}
+ {
+ ///
+ /// Backing BindableProperty for the property.
+ ///
+ public static readonly global::Microsoft.Maui.Controls.BindableProperty TextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Text", typeof(string), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultText);
+ public partial string Text { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingText ? field : (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
+
+ ///
+ /// Backing BindableProperty for the property.
+ ///
+ public static readonly global::Microsoft.Maui.Controls.BindableProperty CustomDurationProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("CustomDuration", typeof(System.TimeSpan), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultCustomDuration);
+ public partial System.TimeSpan CustomDuration { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingCustomDuration ? field : (System.TimeSpan)GetValue(CustomDurationProperty); set => SetValue(CustomDurationProperty, value); }
+ }
+
+ file static class __{{defaultTestClassName}}BindablePropertyInitHelpers
+ {
+ public static bool IsInitializingText = false;
+ public static object CreateDefaultText(global::Microsoft.Maui.Controls.BindableObject bindable)
+ {
+ IsInitializingText = true;
+ var defaultValue = ((TestView)bindable).Text;
+ IsInitializingText = false;
+ return defaultValue;
+ }
+
+ public static bool IsInitializingCustomDuration = false;
+ public static object CreateDefaultCustomDuration(global::Microsoft.Maui.Controls.BindableObject bindable)
+ {
+ IsInitializingCustomDuration = true;
+ var defaultValue = ((TestView)bindable).CustomDuration;
+ IsInitializingCustomDuration = false;
+ return defaultValue;
+ }
+ }
+ """;
+
+ await VerifySourceGeneratorAsync(source, expectedGenerated);
+ }
}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/EdgeCaseTests.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/EdgeCaseTests.cs
index 8de524928e..2fef4c8764 100644
--- a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/EdgeCaseTests.cs
+++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/EdgeCaseTests.cs
@@ -456,4 +456,51 @@ public partial class {{defaultTestClassName}}
await VerifySourceGeneratorAsync(source, expectedGenerated);
}
+
+ [Fact]
+ public async Task GenerateBindableProperty_WithBothInitializerAndDefault_GeneratedCodeDefaultsToUseDefaultValueCreatorMethod()
+ {
+ const string source =
+ /* language=C#-test */
+ //lang=csharp
+ $$"""
+ using CommunityToolkit.Maui;
+ using Microsoft.Maui.Controls;
+ using System;
+
+ namespace {{defaultTestNamespace}};
+
+ public partial class {{defaultTestClassName}} : View
+ {
+ [BindablePropertyAttribute(DefaultValueCreatorMethodName = nameof(CreateDefaultText))]
+ public partial string Text { get; set; } = "Initial Value";
+
+ static string CreateDefaultText(BindableObject bindable)
+ {
+ return "Initial Value";
+ }
+ }
+ """;
+
+ const string expectedGenerated =
+ /* language=C#-test */
+ //lang=csharp
+ $$"""
+ //
+ // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
+ #pragma warning disable
+ #nullable enable
+ namespace {{defaultTestNamespace}};
+ public partial class {{defaultTestClassName}}
+ {
+ ///
+ /// Backing BindableProperty for the property.
+ ///
+ public static readonly global::Microsoft.Maui.Controls.BindableProperty TextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Text", typeof(string), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, CreateDefaultText);
+ public partial string Text { get => false ? field : (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
+ }
+ """;
+
+ await VerifySourceGeneratorAsync(source, expectedGenerated);
+ }
}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyModelTests.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyModelTests.cs
index 7738db8a20..f0df70603e 100644
--- a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyModelTests.cs
+++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyModelTests.cs
@@ -29,7 +29,8 @@ public void BindablePropertyName_ReturnsCorrectPropertyName()
"null",
string.Empty,
true, // IsReadOnlyBindableProperty
- string.Empty // SetterAccessibility
+ string.Empty, // SetterAccessibility
+ false
);
// Act
@@ -56,6 +57,7 @@ public void BindablePropertyModel_WithAllParameters_StoresCorrectValues()
const string coerceValueMethodName = "CoerceValue";
const string defaultValueCreatorMethodName = "CreateDefaultValue";
const string newKeywordText = "new ";
+ const bool hasInitializer = false;
// Act
var model = new BindablePropertyModel(
@@ -71,7 +73,8 @@ public void BindablePropertyModel_WithAllParameters_StoresCorrectValues()
defaultValueCreatorMethodName,
newKeywordText,
true, // IsReadOnlyBindableProperty
- string.Empty // SetterAccessibility
+ string.Empty, // SetterAccessibility
+ hasInitializer
);
// Assert
@@ -86,7 +89,10 @@ public void BindablePropertyModel_WithAllParameters_StoresCorrectValues()
Assert.Equal(coerceValueMethodName, model.CoerceValueMethodName);
Assert.Equal(defaultValueCreatorMethodName, model.DefaultValueCreatorMethodName);
Assert.Equal(newKeywordText, model.NewKeywordText);
+ Assert.Equal(hasInitializer, model.HasInitializer);
Assert.Equal("TestPropertyProperty", model.BindablePropertyName);
+ Assert.Equal(defaultValueCreatorMethodName, model.EffectiveDefaultValueCreatorMethodName);
+ Assert.Equal("IsInitializingTestProperty", model.InitializingPropertyName);
}
[Fact]
@@ -128,7 +134,8 @@ public void SemanticValues_WithClassInfoAndProperties_StoresCorrectValues()
"null",
string.Empty,
true, // IsReadOnlyBindableProperty
- string.Empty // SetterAccessibilityText
+ string.Empty, // SetterAccessibilityText
+ false
);
var bindableProperties = new[] { bindableProperty }.ToImmutableArray();
@@ -156,4 +163,80 @@ static Compilation CreateCompilation(string source)
references,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
}
+
+ [Fact]
+ public void BindablePropertyName_WithInitializer_ReturnsCorrectEffectiveDefaultValueCreatorMethodName()
+ {
+ // Arrange
+ var compilation = CreateCompilation(
+ """
+ public class TestClass { public string TestProperty { get; set; } = "Initial Value"; }");
+ """);
+ var typeSymbol = compilation.GetTypeByMetadataName("TestClass")!;
+ var propertySymbol = typeSymbol.GetMembers("TestProperty").OfType().First();
+
+ const string propertyName = "TestProperty";
+ const bool hasInitializer = true;
+
+ var model = new BindablePropertyModel(
+ propertyName,
+ propertySymbol.Type,
+ typeSymbol,
+ "null",
+ "Microsoft.Maui.Controls.BindingMode.OneWay",
+ "null",
+ "null",
+ "null",
+ "null",
+ "null",
+ string.Empty,
+ true,
+ string.Empty,
+ hasInitializer
+ );
+
+ // Act
+ var effectiveDefaultValueCreatorMethodName = model.EffectiveDefaultValueCreatorMethodName;
+
+ // Assert
+ Assert.Equal("CreateDefault" + propertyName, effectiveDefaultValueCreatorMethodName);
+ }
+
+ [Fact]
+ public void BindablePropertyName_WithInitializerAndDefaulValueCreator_ReturnsCorrectEffectiveDefaultValueCreatorMethodName()
+ {
+ // Arrange
+ var compilation = CreateCompilation(
+ """
+ public class TestClass { public string TestProperty { get; set; } = "Initial Value"; }
+ """);
+ var typeSymbol = compilation.GetTypeByMetadataName("TestClass")!;
+ var propertySymbol = typeSymbol.GetMembers("TestProperty").OfType().First();
+
+ const string propertyName = "TestProperty";
+ const bool hasInitializer = true;
+
+ var model = new BindablePropertyModel(
+ propertyName,
+ propertySymbol.Type,
+ typeSymbol,
+ "null",
+ "Microsoft.Maui.Controls.BindingMode.OneWay",
+ "null",
+ "null",
+ "null",
+ "null",
+ "CreateTextDefaultValue",
+ string.Empty,
+ true,
+ string.Empty,
+ hasInitializer
+ );
+
+ // Act
+ var effectiveDefaultValueCreatorMethodName = model.EffectiveDefaultValueCreatorMethodName;
+
+ // Assert
+ Assert.Equal("CreateTextDefaultValue", effectiveDefaultValueCreatorMethodName);
+ }
}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal/Generators/BindablePropertyAttributeSourceGenerator.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal/Generators/BindablePropertyAttributeSourceGenerator.cs
index c4409da734..b38a572090 100644
--- a/src/CommunityToolkit.Maui.SourceGenerators.Internal/Generators/BindablePropertyAttributeSourceGenerator.cs
+++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal/Generators/BindablePropertyAttributeSourceGenerator.cs
@@ -174,18 +174,43 @@ static string GenerateSource(SemanticValues value)
sb.Append(value.ClassInformation.DeclaredAccessibility).Append(" partial class ").Append(classNameWithGenerics).Append("\n{\n\n");
+ // Prepare helper builder for file-static class members (static flags + default creators)
+ var fileStaticClassStringBuilder = new StringBuilder(256);
+ var helperNames = new HashSet();
+
+ // Build fully-qualified declaring type name for helper method casts (include containing types)
+ var fullDeclaringType = string.IsNullOrEmpty(value.ClassInformation.ContainingTypes)
+ ? classNameWithGenerics
+ : string.Concat(value.ClassInformation.ContainingTypes, ".", classNameWithGenerics);
+
+ var fileStaticClassName = $"__{classNameWithGenerics}BindablePropertyInitHelpers";
+
foreach (var info in value.BindableProperties)
{
if (info.IsReadOnlyBindableProperty)
{
- GenerateReadOnlyBindableProperty(sb, in info);
+ GenerateReadOnlyBindableProperty(sb, in info, fileStaticClassName);
}
else
{
- GenerateBindableProperty(sb, in info);
+ GenerateBindableProperty(sb, in info, fileStaticClassName);
}
- GenerateProperty(sb, in info);
+ if (info.ShouldUsePropertyInitializer)
+ {
+ // Generate only references within the class; actual static field and creator method
+ // will be placed inside the file static helper class below.
+ if (helperNames.Add(info.InitializingPropertyName))
+ {
+ AppendHelperInitializingField(fileStaticClassStringBuilder, in info);
+ }
+ if (helperNames.Add(info.EffectiveDefaultValueCreatorMethodName))
+ {
+ AppendHelperDefaultValueMethod(fileStaticClassStringBuilder, in info, fullDeclaringType);
+ }
+ }
+
+ GenerateProperty(sb, in info, fileStaticClassName);
}
sb.Append('}');
@@ -200,11 +225,19 @@ static string GenerateSource(SemanticValues value)
}
}
+ // If we generated any helper members, emit a file static class with them.
+ if (fileStaticClassStringBuilder.Length > 0)
+ {
+ sb.Append("\n\nfile static class ").Append(fileStaticClassName).Append("\n{\n");
+ sb.Append(fileStaticClassStringBuilder.ToString());
+ sb.Append("}\n");
+ }
+
return sb.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- static void GenerateReadOnlyBindableProperty(StringBuilder sb, in BindablePropertyModel info)
+ static void GenerateReadOnlyBindableProperty(StringBuilder sb, in BindablePropertyModel info, in string fileStaticClassName)
{
// Sanitize the Return Type because Nullable Reference Types cannot be used in the `typeof()` operator
var nonNullableReturnType = ConvertToNonNullableTypeSymbol(info.ReturnType);
@@ -237,8 +270,15 @@ static void GenerateReadOnlyBindableProperty(StringBuilder sb, in BindableProper
.Append(info.PropertyChangingMethodName)
.Append(", ")
.Append(info.CoerceValueMethodName)
- .Append(", ")
- .Append(info.DefaultValueCreatorMethodName)
+ .Append(", ");
+
+ if (info.ShouldUsePropertyInitializer)
+ {
+ sb.Append(fileStaticClassName)
+ .Append('.');
+ }
+
+ sb.Append(info.EffectiveDefaultValueCreatorMethodName)
.Append(");\n");
// Generate public BindableProperty from the key
@@ -256,7 +296,7 @@ static void GenerateReadOnlyBindableProperty(StringBuilder sb, in BindableProper
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- static void GenerateBindableProperty(StringBuilder sb, in BindablePropertyModel info)
+ static void GenerateBindableProperty(StringBuilder sb, in BindablePropertyModel info, in string helperClassName)
{
// Sanitize the Return Type because Nullable Reference Types cannot be used in the `typeof()` operator
var nonNullableReturnType = ConvertToNonNullableTypeSymbol(info.ReturnType);
@@ -293,15 +333,21 @@ static void GenerateBindableProperty(StringBuilder sb, in BindablePropertyModel
.Append(info.PropertyChangingMethodName)
.Append(", ")
.Append(info.CoerceValueMethodName)
- .Append(", ")
- .Append(info.DefaultValueCreatorMethodName)
- .Append(");\n");
+ .Append(", ");
- sb.Append('\n');
+ if (info.ShouldUsePropertyInitializer)
+ {
+ sb.Append(helperClassName)
+ .Append('.');
+ }
+
+ sb.Append(info.EffectiveDefaultValueCreatorMethodName)
+ .Append(");\n")
+ .Append('\n');
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- static void GenerateProperty(StringBuilder sb, in BindablePropertyModel info)
+ static void GenerateProperty(StringBuilder sb, in BindablePropertyModel info, in string fileStaticClassName)
{
var sanitizedPropertyName = IsDotnetKeyword(info.PropertyName) ? string.Concat("@", info.PropertyName) : info.PropertyName;
var formattedReturnType = GetFormattedReturnType(info.ReturnType);
@@ -312,7 +358,24 @@ static void GenerateProperty(StringBuilder sb, in BindablePropertyModel info)
.Append(formattedReturnType)
.Append(' ')
.Append(sanitizedPropertyName)
- .Append("\n{\nget => (")
+ .Append("\n{\nget => ");
+
+ if (info.HasInitializer)
+ {
+ if (info.ShouldUsePropertyInitializer)
+ {
+ // Now reference the static flag on the file static helper class
+ sb.Append(fileStaticClassName).Append(".").Append(info.InitializingPropertyName);
+ }
+ else
+ {
+ sb.Append("false");
+ }
+
+ sb.Append(" ? field : ");
+ }
+
+ sb.Append("(")
.Append(formattedReturnType)
.Append(")GetValue(")
.Append(info.BindablePropertyName)
@@ -335,6 +398,7 @@ static SemanticValues SemanticTransform(GeneratorAttributeSyntaxContext context,
var propertyDeclarationSyntax = Unsafe.As(context.TargetNode);
var semanticModel = context.SemanticModel;
var propertySymbol = (IPropertySymbol?)ModelExtensions.GetDeclaredSymbol(semanticModel, propertyDeclarationSyntax, cancellationToken);
+ var hasInitializer = propertyDeclarationSyntax.Initializer is not null;
if (propertySymbol is null)
{
@@ -361,7 +425,7 @@ static SemanticValues SemanticTransform(GeneratorAttributeSyntaxContext context,
var (isReadOnlyBindableProperty, setterAccessibility) = GetPropertyAccessibility(propertySymbol, propertyDeclarationSyntax);
var attributeData = context.Attributes[0];
- bindablePropertyModels[0] = CreateBindablePropertyModel(attributeData, propertySymbol.ContainingType, propertySymbol.Name, returnType, doesContainNewKeyword, isReadOnlyBindableProperty, setterAccessibility);
+ bindablePropertyModels[0] = CreateBindablePropertyModel(attributeData, propertySymbol.ContainingType, propertySymbol.Name, returnType, doesContainNewKeyword, isReadOnlyBindableProperty, setterAccessibility, hasInitializer);
return new(propertyInfo, ImmutableArray.Create(bindablePropertyModels));
}
@@ -458,7 +522,7 @@ static string GetGenericTypeParameters(INamedTypeSymbol typeSymbol)
return sb.ToString();
}
- static BindablePropertyModel CreateBindablePropertyModel(in AttributeData attributeData, in INamedTypeSymbol declaringType, in string propertyName, in ITypeSymbol returnType, in bool doesContainNewKeyword, in bool isReadOnly, in string? setterAccessibility)
+ static BindablePropertyModel CreateBindablePropertyModel(in AttributeData attributeData, in INamedTypeSymbol declaringType, in string propertyName, in ITypeSymbol returnType, in bool doesContainNewKeyword, in bool isReadOnly, in string? setterAccessibility, in bool hasInitializer)
{
if (attributeData.AttributeClass is null)
{
@@ -474,7 +538,7 @@ static BindablePropertyModel CreateBindablePropertyModel(in AttributeData attrib
var validateValueMethodName = attributeData.GetNamedMethodGroupArgumentsAttributeValueByNameAsString(nameof(BindablePropertyModel.ValidateValueMethodName));
var newKeywordText = doesContainNewKeyword ? "new " : string.Empty;
- return new BindablePropertyModel(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValueMethodName, propertyChangedMethodName, propertyChangingMethodName, coerceValueMethodName, defaultValueCreatorMethodName, newKeywordText, isReadOnly, setterAccessibility);
+ return new BindablePropertyModel(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValueMethodName, propertyChangedMethodName, propertyChangingMethodName, coerceValueMethodName, defaultValueCreatorMethodName, newKeywordText, isReadOnly, setterAccessibility, hasInitializer);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -531,4 +595,46 @@ static string GetFormattedReturnType(ITypeSymbol typeSymbol)
return typeSymbol.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat);
}
}
+
+ ///
+ /// Appends the initializing flag into the file-static helper class.
+ ///
+ /// Helper StringBuilder used to collect helper members.
+ /// Property model.
+ static void AppendHelperInitializingField(StringBuilder fileStaticClassStringBuilder, in BindablePropertyModel info)
+ {
+ // Make the flag public static so it can be referenced from the generated partial class in the same file.
+ fileStaticClassStringBuilder.Append("public static bool ")
+ .Append(info.InitializingPropertyName)
+ .Append(" = false;\n");
+ }
+
+ ///
+ /// Appends a default value creator method into the file-static helper class.
+ /// The method sets the static initializing flag, reads the property's initializer value by casting the bindable
+ /// to the declaring type, then clears the flag and returns the value.
+ ///
+ /// Helper StringBuilder used to collect helper members.
+ /// Property model.
+ /// Declaring type including containing types and generic parameters.
+ static void AppendHelperDefaultValueMethod(StringBuilder fileStaticClassStringBuilder, in BindablePropertyModel info, string fullDeclaringType)
+ {
+ var sanitizedPropertyName = IsDotnetKeyword(info.PropertyName) ? string.Concat("@", info.PropertyName) : info.PropertyName;
+
+ fileStaticClassStringBuilder.Append("public static object ")
+ .Append(info.EffectiveDefaultValueCreatorMethodName)
+ .Append("(global::Microsoft.Maui.Controls.BindableObject bindable)\n")
+ .Append("{\n")
+ .Append(info.InitializingPropertyName)
+ .Append(" = true;\n")
+ .Append("var defaultValue = ((")
+ .Append(fullDeclaringType)
+ .Append(")bindable).")
+ .Append(sanitizedPropertyName)
+ .Append(";\n")
+ .Append(info.InitializingPropertyName)
+ .Append(" = false;\n")
+ .Append("return defaultValue;\n")
+ .Append("}\n\n");
+ }
}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal/Models/Records.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal/Models/Records.cs
index e2ead63c39..ddc946229b 100644
--- a/src/CommunityToolkit.Maui.SourceGenerators.Internal/Models/Records.cs
+++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal/Models/Records.cs
@@ -3,10 +3,15 @@
namespace CommunityToolkit.Maui.SourceGenerators.Internal.Models;
-public record BindablePropertyModel(string PropertyName, ITypeSymbol ReturnType, ITypeSymbol DeclaringType, string DefaultValue, string DefaultBindingMode, string ValidateValueMethodName, string PropertyChangedMethodName, string PropertyChangingMethodName, string CoerceValueMethodName, string DefaultValueCreatorMethodName, string NewKeywordText, bool IsReadOnlyBindableProperty, string? SetterAccessibility)
+public record BindablePropertyModel(string PropertyName, ITypeSymbol ReturnType, ITypeSymbol DeclaringType, string DefaultValue, string DefaultBindingMode, string ValidateValueMethodName, string PropertyChangedMethodName, string PropertyChangingMethodName, string CoerceValueMethodName, string DefaultValueCreatorMethodName, string NewKeywordText, bool IsReadOnlyBindableProperty, string? SetterAccessibility, bool HasInitializer)
{
+ // When both a DefaultValueCreatorMethodName and an initializer are provided, we implement the DefaultValueCreator method and the ignore the partial Property initializer
+ public bool ShouldUsePropertyInitializer => HasInitializer && DefaultValueCreatorMethodName is "null";
public string BindablePropertyName => $"{PropertyName}Property";
public string BindablePropertyKeyName => $"{char.ToLower(PropertyName[0])}{PropertyName[1..]}PropertyKey";
+ public string EffectiveDefaultValueCreatorMethodName => ShouldUsePropertyInitializer ? $"CreateDefault{PropertyName}" : DefaultValueCreatorMethodName;
+ public string InitializingPropertyName => $"IsInitializing{PropertyName}";
+
}
public record SemanticValues(ClassInformation ClassInformation, EquatableArray BindableProperties);
diff --git a/src/CommunityToolkit.Maui.UnitTests/Views/AvatarView/AvatarViewInterfaceTests.cs b/src/CommunityToolkit.Maui.UnitTests/Views/AvatarView/AvatarViewInterfaceTests.cs
index 1223da88ff..777163852b 100644
--- a/src/CommunityToolkit.Maui.UnitTests/Views/AvatarView/AvatarViewInterfaceTests.cs
+++ b/src/CommunityToolkit.Maui.UnitTests/Views/AvatarView/AvatarViewInterfaceTests.cs
@@ -15,21 +15,21 @@ public AvatarViewInterfaceTests()
public void IBorderElementBorderColorDefaultValue()
{
var avatarView = new Maui.Views.AvatarView();
- ((IBorderElement)avatarView).BorderColorDefaultValue.Should().Be(AvatarViewDefaults.DefaultBorderColor);
+ ((IBorderElement)avatarView).BorderColorDefaultValue.Should().Be(AvatarViewDefaults.BorderColor);
}
[Fact]
public void IBorderElementBorderWidthDefaultValue()
{
var avatarView = new Maui.Views.AvatarView();
- ((IBorderElement)avatarView).BorderWidthDefaultValue.Should().Be(AvatarViewDefaults.DefaultBorderWidth);
+ ((IBorderElement)avatarView).BorderWidthDefaultValue.Should().Be(AvatarViewDefaults.BorderWidth);
}
[Fact]
public void IBorderElementCornerRadius()
{
var avatarView = new Maui.Views.AvatarView();
- int average = (int)(new[] { AvatarViewDefaults.DefaultCornerRadius.TopLeft, AvatarViewDefaults.DefaultCornerRadius.TopRight, AvatarViewDefaults.DefaultCornerRadius.BottomLeft, AvatarViewDefaults.DefaultCornerRadius.BottomRight }).Average();
+ int average = (int)(new[] { AvatarViewDefaults.CornerRadius.TopLeft, AvatarViewDefaults.CornerRadius.TopRight, AvatarViewDefaults.CornerRadius.BottomLeft, AvatarViewDefaults.CornerRadius.BottomRight }).Average();
((IBorderElement)avatarView).CornerRadius.Should().Be(average);
CornerRadius cornerRadius = new(3, 7, 37, 73);
average = (int)(new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomLeft, cornerRadius.BottomRight }).Average();
@@ -41,7 +41,7 @@ public void IBorderElementCornerRadius()
public void IBorderElementCornerRadiusDefaultValue()
{
var avatarView = new Maui.Views.AvatarView();
- int average = (int)(new[] { AvatarViewDefaults.DefaultCornerRadius.TopLeft, AvatarViewDefaults.DefaultCornerRadius.TopRight, AvatarViewDefaults.DefaultCornerRadius.BottomLeft, AvatarViewDefaults.DefaultCornerRadius.BottomRight }).Average();
+ int average = (int)(new[] { AvatarViewDefaults.CornerRadius.TopLeft, AvatarViewDefaults.CornerRadius.TopRight, AvatarViewDefaults.CornerRadius.BottomLeft, AvatarViewDefaults.CornerRadius.BottomRight }).Average();
((IBorderElement)avatarView).CornerRadiusDefaultValue.Should().Be(average);
}
@@ -235,7 +235,7 @@ public void IsCornerRadiusSet()
public void IBorderElementOnBorderColorPropertyChanged()
{
var avatarView = new Maui.Views.AvatarView();
- avatarView.Stroke.Should().Be((SolidColorBrush)AvatarViewDefaults.DefaultBorderColor);
+ avatarView.Stroke.Should().Be((SolidColorBrush)AvatarViewDefaults.BorderColor);
((IBorderElement)avatarView).OnBorderColorPropertyChanged(Colors.AliceBlue, Colors.Azure);
avatarView.Stroke.Should().Be((SolidColorBrush)Colors.Azure);
}
diff --git a/src/CommunityToolkit.Maui.UnitTests/Views/AvatarView/AvatarViewTests.cs b/src/CommunityToolkit.Maui.UnitTests/Views/AvatarView/AvatarViewTests.cs
index bde79255a2..0690d1c576 100644
--- a/src/CommunityToolkit.Maui.UnitTests/Views/AvatarView/AvatarViewTests.cs
+++ b/src/CommunityToolkit.Maui.UnitTests/Views/AvatarView/AvatarViewTests.cs
@@ -13,6 +13,25 @@ public AvatarViewTests()
Assert.IsType(new Maui.Views.AvatarView(), exactMatch: false);
}
+ [Fact]
+ public void MultipleAvatarViews_BindablePropertyAttributedProperties_WithPartialPropertyInializers_UseCorrectDefaultValue()
+ {
+ // Arrange
+ var avatarView1 = new Maui.Views.AvatarView();
+ var avatarView2 = new Maui.Views.AvatarView();
+
+ // Act Assert
+ Assert.Equal(AvatarViewDefaults.BorderColor, avatarView1.BorderColor);
+ Assert.Equal(AvatarViewDefaults.BorderWidth, avatarView1.BorderWidth);
+ Assert.Equal(AvatarViewDefaults.CornerRadius, avatarView1.CornerRadius);
+ Assert.Equal(AvatarViewDefaults.Text, avatarView1.Text);
+
+ Assert.Equal(AvatarViewDefaults.BorderColor, avatarView2.BorderColor);
+ Assert.Equal(AvatarViewDefaults.BorderWidth, avatarView2.BorderWidth);
+ Assert.Equal(AvatarViewDefaults.CornerRadius, avatarView2.CornerRadius);
+ Assert.Equal(AvatarViewDefaults.Text, avatarView2.Text);
+ }
+
[Fact]
public void AvatarViewShouldBeAssignedToIAvatarView()
{
@@ -164,21 +183,21 @@ public void CornerRadiusSameRadiusToThree()
public void DefaultBorderColor()
{
var avatarView = new Maui.Views.AvatarView();
- avatarView.BorderColor.Should().Be(AvatarViewDefaults.DefaultBorderColor);
+ avatarView.BorderColor.Should().Be(AvatarViewDefaults.BorderColor);
}
[Fact]
public void DefaultBorderWidth()
{
var avatarView = new Maui.Views.AvatarView();
- avatarView.BorderWidth.Should().Be(AvatarViewDefaults.DefaultBorderWidth);
+ avatarView.BorderWidth.Should().Be(AvatarViewDefaults.BorderWidth);
}
[Fact]
public void DefaultCornerRadius()
{
var avatarView = new Maui.Views.AvatarView();
- avatarView.CornerRadius.Should().Be(new CornerRadius(AvatarViewDefaults.DefaultCornerRadius.TopLeft, AvatarViewDefaults.DefaultCornerRadius.TopRight, AvatarViewDefaults.DefaultCornerRadius.BottomLeft, AvatarViewDefaults.DefaultCornerRadius.BottomRight));
+ avatarView.CornerRadius.Should().Be(new CornerRadius(AvatarViewDefaults.CornerRadius.TopLeft, AvatarViewDefaults.CornerRadius.TopRight, AvatarViewDefaults.CornerRadius.BottomLeft, AvatarViewDefaults.CornerRadius.BottomRight));
}
[Fact]
@@ -195,7 +214,7 @@ public void DefaultHeightRequest()
CreateViewHandler(avatarView);
Size request = avatarView.Measure(double.PositiveInfinity, double.PositiveInfinity);
- request.Height.Should().Be(AvatarViewDefaults.DefaultHeightRequest);
+ request.Height.Should().Be(AvatarViewDefaults.HeightRequest);
}
[Fact]
@@ -208,7 +227,7 @@ public void DefaultLabelProperties()
avatarLabel.Should().NotBeNull();
avatarLabel.HorizontalTextAlignment.Should().Be(TextAlignment.Center);
avatarLabel.VerticalTextAlignment.Should().Be(TextAlignment.Center);
- avatarLabel.Text.Should().Be(AvatarViewDefaults.DefaultText);
+ avatarLabel.Text.Should().Be(AvatarViewDefaults.Text);
}
}
@@ -219,11 +238,11 @@ public void DefaultProperties()
avatarView.IsEnabled.Should().BeTrue();
avatarView.HorizontalOptions.Should().Be(LayoutOptions.Center);
avatarView.VerticalOptions.Should().Be(LayoutOptions.Center);
- avatarView.HeightRequest.Should().Be(AvatarViewDefaults.DefaultHeightRequest);
- avatarView.WidthRequest.Should().Be(AvatarViewDefaults.DefaultWidthRequest);
- avatarView.Padding.Should().Be(AvatarViewDefaults.DefaultPadding);
- avatarView.Stroke.Should().Be((SolidColorBrush)AvatarViewDefaults.DefaultBorderColor);
- avatarView.StrokeThickness.Should().Be(AvatarViewDefaults.DefaultBorderWidth);
+ avatarView.HeightRequest.Should().Be(AvatarViewDefaults.HeightRequest);
+ avatarView.WidthRequest.Should().Be(AvatarViewDefaults.WidthRequest);
+ avatarView.Padding.Should().Be(AvatarViewDefaults.Padding);
+ avatarView.Stroke.Should().Be((SolidColorBrush)AvatarViewDefaults.BorderColor);
+ avatarView.StrokeThickness.Should().Be(AvatarViewDefaults.BorderWidth);
avatarView.StrokeShape.Should().BeOfType();
}
@@ -234,7 +253,7 @@ public void DefaultWidthRequest()
CreateViewHandler(avatarView);
Size request = avatarView.Measure(double.PositiveInfinity, double.PositiveInfinity);
- request.Width.Should().Be(AvatarViewDefaults.DefaultWidthRequest);
+ request.Width.Should().Be(AvatarViewDefaults.WidthRequest);
}
[Fact]
diff --git a/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/ImageTouch/ImageTouchBehavior.shared.cs b/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/ImageTouch/ImageTouchBehavior.shared.cs
index d2e5e2100a..c41225af79 100644
--- a/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/ImageTouch/ImageTouchBehavior.shared.cs
+++ b/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/ImageTouch/ImageTouchBehavior.shared.cs
@@ -11,42 +11,42 @@ public partial class ImageTouchBehavior : TouchBehavior
///
/// Gets or sets the when is .
///
- [BindableProperty(DefaultValue = ImageTouchBehaviorDefaults.DefaultBackgroundImageSource)]
- public partial ImageSource? DefaultImageSource { get; set; }
+ [BindableProperty]
+ public partial ImageSource? DefaultImageSource { get; set; } = (ImageSource?)ImageTouchBehaviorDefaults.DefaultBackgroundImageSource;
///
/// Gets or sets the when the is
///
- [BindableProperty(DefaultValue = ImageTouchBehaviorDefaults.HoveredBackgroundImageSource)]
- public partial ImageSource? HoveredImageSource { get; set; }
+ [BindableProperty]
+ public partial ImageSource? HoveredImageSource { get; set; } = (ImageSource?)ImageTouchBehaviorDefaults.HoveredBackgroundImageSource;
///
/// Gets or sets the when the is
///
- [BindableProperty(DefaultValue = ImageTouchBehaviorDefaults.PressedBackgroundImageSource)]
- public partial ImageSource? PressedImageSource { get; set; }
+ [BindableProperty]
+ public partial ImageSource? PressedImageSource { get; set; } = (ImageSource?)ImageTouchBehaviorDefaults.PressedBackgroundImageSource;
///
/// Gets or sets the when is .
///
- [BindableProperty(DefaultValue = ImageTouchBehaviorDefaults.DefaultBackgroundImageAspect)]
- public partial Aspect DefaultImageAspect { get; set; }
+ [BindableProperty]
+ public partial Aspect DefaultImageAspect { get; set; } = ImageTouchBehaviorDefaults.DefaultBackgroundImageAspect;
///
/// Gets or sets the when is .
///
- [BindableProperty(DefaultValue = ImageTouchBehaviorDefaults.HoveredBackgroundImageAspect)]
- public partial Aspect HoveredImageAspect { get; set; }
+ [BindableProperty]
+ public partial Aspect HoveredImageAspect { get; set; } = ImageTouchBehaviorDefaults.HoveredBackgroundImageAspect;
///
/// Gets or sets the when the is
///
- [BindableProperty(DefaultValue = ImageTouchBehaviorDefaults.PressedBackgroundImageAspect)]
- public partial Aspect PressedImageAspect { get; set; }
+ [BindableProperty]
+ public partial Aspect PressedImageAspect { get; set; } = ImageTouchBehaviorDefaults.PressedBackgroundImageAspect;
///
/// Gets or sets a value indicating whether the image should be set when the animation ends.
///
- [BindableProperty(DefaultValue = ImageTouchBehaviorDefaults.ShouldSetImageOnAnimationEnd)]
- public partial bool ShouldSetImageOnAnimationEnd { get; set; }
+ [BindableProperty]
+ public partial bool ShouldSetImageOnAnimationEnd { get; set; } = ImageTouchBehaviorDefaults.ShouldSetImageOnAnimationEnd;
}
\ No newline at end of file
diff --git a/src/CommunityToolkit.Maui/Views/AvatarView.shared.cs b/src/CommunityToolkit.Maui/Views/AvatarView.shared.cs
index 37ecc433ea..4c7a8cd2ed 100644
--- a/src/CommunityToolkit.Maui/Views/AvatarView.shared.cs
+++ b/src/CommunityToolkit.Maui/Views/AvatarView.shared.cs
@@ -9,15 +9,6 @@ namespace CommunityToolkit.Maui.Views;
/// AvatarView control.
public partial class AvatarView : Border, IAvatarView, IBorderElement, IFontElement, ITextElement, IImageElement, ITextAlignmentElement, ILineHeightElement, ICornerElement
{
- /// The backing store for the bindable property.
- public static readonly BindableProperty BorderColorProperty = BindableProperty.Create(nameof(BorderColor), typeof(Color), typeof(IAvatarView), defaultValue: AvatarViewDefaults.DefaultBorderColor, propertyChanged: OnBorderColorPropertyChanged);
-
- /// The backing store for the bindable property.
- public static readonly BindableProperty BorderWidthProperty = BindableProperty.Create(nameof(BorderWidth), typeof(double), typeof(IAvatarView), defaultValue: AvatarViewDefaults.DefaultBorderWidth, propertyChanged: OnBorderWidthPropertyChanged);
-
- /// The backing store for the bindable property.
- public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(CornerRadius), typeof(ICornerElement), defaultValue: AvatarViewDefaults.DefaultCornerRadius, propertyChanged: OnCornerRadiusPropertyChanged);
-
/// The backing store for the bindable property.
public static readonly BindableProperty FontAttributesProperty = FontElement.FontAttributesProperty;
@@ -30,15 +21,9 @@ public partial class AvatarView : Border, IAvatarView, IBorderElement, IFontElem
/// The backing store for the bindable property.
public static readonly BindableProperty FontSizeProperty = FontElement.FontSizeProperty;
- /// The backing store for the bindable property.
- public static readonly BindableProperty ImageSourceProperty = BindableProperty.Create(nameof(ImageSource), typeof(ImageSource), typeof(IImageElement), default(ImageSource), propertyChanged: OnImageSourcePropertyChanged);
-
/// The backing store for the bindable property.
public static readonly BindableProperty TextColorProperty = TextElement.TextColorProperty;
- /// The backing store for the bindable property.
- public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(AvatarView), defaultValue: AvatarViewDefaults.DefaultText, propertyChanged: OnTextPropertyChanged);
-
/// The backing store for the bindable property.
public static readonly BindableProperty TextTransformProperty = TextElement.TextTransformProperty;
@@ -51,7 +36,7 @@ public partial class AvatarView : Border, IAvatarView, IBorderElement, IFontElem
{
HorizontalTextAlignment = TextAlignment.Center,
VerticalTextAlignment = TextAlignment.Center,
- Text = AvatarViewDefaults.DefaultText,
+ Text = AvatarViewDefaults.Text,
};
bool wasImageLoading;
@@ -63,14 +48,14 @@ public AvatarView()
IsEnabled = true;
HorizontalOptions = VerticalOptions = LayoutOptions.Center;
- HeightRequest = AvatarViewDefaults.DefaultHeightRequest;
- WidthRequest = AvatarViewDefaults.DefaultWidthRequest;
- Padding = AvatarViewDefaults.DefaultPadding;
- Stroke = AvatarViewDefaults.DefaultBorderColor;
- StrokeThickness = AvatarViewDefaults.DefaultBorderWidth;
+ HeightRequest = AvatarViewDefaults.HeightRequest;
+ WidthRequest = AvatarViewDefaults.WidthRequest;
+ Padding = AvatarViewDefaults.Padding;
+ Stroke = AvatarViewDefaults.BorderColor;
+ StrokeThickness = AvatarViewDefaults.BorderWidth;
StrokeShape = new RoundRectangle
{
- CornerRadius = new CornerRadius(AvatarViewDefaults.DefaultCornerRadius.TopLeft, AvatarViewDefaults.DefaultCornerRadius.TopRight, AvatarViewDefaults.DefaultCornerRadius.BottomLeft, AvatarViewDefaults.DefaultCornerRadius.BottomRight),
+ CornerRadius = new CornerRadius(AvatarViewDefaults.CornerRadius.TopLeft, AvatarViewDefaults.CornerRadius.TopRight, AvatarViewDefaults.CornerRadius.BottomLeft, AvatarViewDefaults.CornerRadius.BottomRight),
};
Content = avatarLabel;
avatarImage.SetBinding(WidthRequestProperty, BindingBase.Create(static p => p.WidthRequest, source: this));
@@ -80,20 +65,6 @@ public AvatarView()
/// Gets or sets the control font.
public Microsoft.Maui.Font Font { get; set; } = Microsoft.Maui.Font.SystemFontOfSize((double)FontElement.FontSizeProperty.DefaultValue);
- /// Gets or sets a value of the control border colour.
- public Color BorderColor
- {
- get => (Color)GetValue(BorderColorProperty);
- set => SetValue(BorderColorProperty, value);
- }
-
- /// Gets or sets a value of the control border width.
- public double BorderWidth
- {
- get => (double)GetValue(BorderWidthProperty);
- set => SetValue(BorderWidthProperty, value);
- }
-
/// Gets or sets a value of the control text character spacing property.
public double CharacterSpacing
{
@@ -101,13 +72,6 @@ public double CharacterSpacing
set => SetValue(TextElement.CharacterSpacingProperty, value);
}
- /// Gets or sets a value of the control corner radius property.
- public CornerRadius CornerRadius
- {
- get => (CornerRadius)GetValue(CornerRadiusProperty);
- set => SetValue(CornerRadiusProperty, value);
- }
-
/// Gets or sets a value of the control font attributes property.
public FontAttributes FontAttributes
{
@@ -137,21 +101,6 @@ public double FontSize
set => SetValue(FontElement.FontSizeProperty, value);
}
- /// Gets or sets a value of the control image source property.
- [TypeConverter(typeof(ImageSourceConverter))]
- public ImageSource ImageSource
- {
- get => (ImageSource)GetValue(ImageSourceProperty);
- set => SetValue(ImageSourceProperty, value);
- }
-
- /// Gets or sets a value of the control text property.
- public string Text
- {
- get => (string)GetValue(TextProperty);
- set => SetValue(TextProperty, value);
- }
-
/// Gets or sets a value of the control text colour property.
public Color TextColor
{
@@ -166,17 +115,38 @@ public TextTransform TextTransform
set => SetValue(TextElement.TextTransformProperty, value);
}
+ /// Gets or sets a value of the control border colour.
+ [BindableProperty(PropertyChangedMethodName = nameof(OnBorderColorPropertyChanged))]
+ public partial Color BorderColor { get; set; } = AvatarViewDefaults.BorderColor;
+
+ /// Gets or sets a value of the control border width.
+ [BindableProperty(PropertyChangedMethodName = nameof(OnBorderWidthPropertyChanged))]
+ public partial double BorderWidth { get; set; } = AvatarViewDefaults.BorderWidth;
+
+ /// Gets or sets a value of the control corner radius property.
+ [BindableProperty(PropertyChangedMethodName = nameof(OnCornerRadiusPropertyChanged))]
+ public partial CornerRadius CornerRadius { get; set; } = AvatarViewDefaults.CornerRadius;
+
+ /// Gets or sets a value of the control image source property.
+ [TypeConverter(typeof(ImageSourceConverter))]
+ [BindableProperty(PropertyChangedMethodName = nameof(OnImageSourcePropertyChanged))]
+ public partial ImageSource? ImageSource { get; set; }
+
+ /// Gets or sets a value of the control text property.
+ [BindableProperty(PropertyChangedMethodName = nameof(OnTextPropertyChanged))]
+ public partial string Text { get; set; } = AvatarViewDefaults.Text;
+
Aspect Microsoft.Maui.IImage.Aspect => ((IImageElement)this).Aspect;
Aspect IImageElement.Aspect => avatarImage.Aspect;
- Color IBorderElement.BorderColorDefaultValue => (Color)BorderColorProperty.DefaultValue;
+ Color IBorderElement.BorderColorDefaultValue => AvatarViewDefaults.BorderColor;
- double IBorderElement.BorderWidthDefaultValue => (double)BorderWidthProperty.DefaultValue;
+ double IBorderElement.BorderWidthDefaultValue => AvatarViewDefaults.BorderWidth;
int IBorderElement.CornerRadius => (int)GetAverageCorderRadius(CornerRadius);
- int IBorderElement.CornerRadiusDefaultValue => (int)GetAverageCorderRadius((CornerRadius)CornerRadiusProperty.DefaultValue);
+ int IBorderElement.CornerRadiusDefaultValue => (int)GetAverageCorderRadius(AvatarViewDefaults.CornerRadius);
TextAlignment ITextAlignment.HorizontalTextAlignment => ((ITextAlignmentElement)this).HorizontalTextAlignment;
@@ -198,7 +168,7 @@ public TextTransform TextTransform
double ILineHeightElement.LineHeight => avatarLabel.LineHeight;
- IImageSource IImageSourcePart.Source => ImageSource;
+ IImageSource? IImageSourcePart.Source => ImageSource;
ImageSource IImageElement.Source => avatarImage.Source;
@@ -344,15 +314,15 @@ void HandlePropertyChanged(object? sender, PropertyChangedEventArgs e)
{
// Ensure avatarImage is clipped to the bounds of the AvatarView whenever its Height, Width, CornerRadius and Padding properties change
if ((e.PropertyName == HeightProperty.PropertyName
- || e.PropertyName == WidthProperty.PropertyName
- || e.PropertyName == PaddingProperty.PropertyName
- || e.PropertyName == ImageSourceProperty.PropertyName
- || e.PropertyName == BorderWidthProperty.PropertyName
- || e.PropertyName == CornerRadiusProperty.PropertyName
- || e.PropertyName == StrokeThicknessProperty.PropertyName)
- && Height >= 0 // The default value of Height (before the view is drawn onto the page) is -1
- && Width >= 0 // The default value of Y (before the view is drawn onto the page) is -1
- && avatarImage.Source is not null)
+ || e.PropertyName == WidthProperty.PropertyName
+ || e.PropertyName == PaddingProperty.PropertyName
+ || e.PropertyName == ImageSourceProperty.PropertyName
+ || e.PropertyName == BorderWidthProperty.PropertyName
+ || e.PropertyName == CornerRadiusProperty.PropertyName
+ || e.PropertyName == StrokeThicknessProperty.PropertyName)
+ && Height >= 0 // The default value of Height (before the view is drawn onto the page) is -1
+ && Width >= 0 // The default value of Y (before the view is drawn onto the page) is -1
+ && avatarImage.Source is not null)
{
Geometry? avatarImageClipGeometry = null;
diff --git a/src/CommunityToolkit.Maui/Views/RatingView/RatingView.shared.cs b/src/CommunityToolkit.Maui/Views/RatingView/RatingView.shared.cs
index 8e677c2a18..f17dbc5fa1 100644
--- a/src/CommunityToolkit.Maui/Views/RatingView/RatingView.shared.cs
+++ b/src/CommunityToolkit.Maui/Views/RatingView/RatingView.shared.cs
@@ -38,21 +38,21 @@ public event EventHandler RatingChanged
/// The path data should be provided in a format compatible with the rendering system, such as SVG path
/// syntax. If the value is null or empty, no custom shape will be applied.
[BindableProperty(PropertyChangedMethodName = nameof(OnCustomShapePathPropertyChanged))]
- public partial string CustomShapePath { get; set; }
+ public partial string? CustomShapePath { get; set; }
///
/// Gets or sets the padding applied to the shape's content area.
///
- [BindableProperty(PropertyChangedMethodName = nameof(OnShapePaddingPropertyChanged), DefaultValueCreatorMethodName = nameof(CreateDefaultShapePadding))]
- public partial Thickness ShapePadding { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnShapePaddingPropertyChanged))]
+ public partial Thickness ShapePadding { get; set; } = RatingViewDefaults.ShapePadding;
///
/// Gets or sets the shape used to display each rating item in the view.
///
/// Changing this property updates the visual appearance of the rating items. The available shapes are
/// defined by the enumeration.
- [BindableProperty(DefaultValue = RatingViewDefaults.Shape, PropertyChangedMethodName = nameof(OnShapePropertyChanged))]
- public partial RatingViewShape Shape { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnShapePropertyChanged))]
+ public partial RatingViewShape Shape { get; set; } = RatingViewDefaults.Shape;
///
/// Gets or sets the color used to draw the border of the shape.
@@ -61,22 +61,22 @@ public event EventHandler RatingChanged
/// This uses a non-nullable . A value will be converted to
///
[AllowNull]
- [BindableProperty(PropertyChangedMethodName = nameof(OnShapeBorderColorChanged), CoerceValueMethodName = nameof(CoerceColorToTransparent), DefaultValueCreatorMethodName = nameof(CreateDefaultShapeBorderColor))]
- public partial Color ShapeBorderColor { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnShapeBorderColorChanged), CoerceValueMethodName = nameof(CoerceColorToTransparent))]
+ public partial Color ShapeBorderColor { get; set; } = RatingViewDefaults.ShapeBorderColor;
///
/// Gets or sets the thickness of the border applied to the shape, in device-independent units.
///
/// A value of 0 indicates that no border will be rendered. Negative values are not supported and may
/// result in undefined behavior.
- [BindableProperty(DefaultValue = RatingViewDefaults.ShapeBorderThickness, PropertyChangedMethodName = nameof(OnShapeBorderThicknessChanged), PropertyChangingMethodName = nameof(OnShapeBorderThicknessChanging))]
- public partial double ShapeBorderThickness { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnShapeBorderThicknessChanged), PropertyChangingMethodName = nameof(OnShapeBorderThicknessChanging))]
+ public partial double ShapeBorderThickness { get; set; } = RatingViewDefaults.ShapeBorderThickness;
///
/// Gets or sets the diameter of the shape, in device-independent units.
///
- [BindableProperty(DefaultValue = RatingViewDefaults.ItemShapeSize, PropertyChangedMethodName = nameof(OnShapeDiameterSizeChanged))]
- public partial double ShapeDiameter { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnShapeDiameterSizeChanged))]
+ public partial double ShapeDiameter { get; set; } = RatingViewDefaults.ItemShapeSize;
///
/// Gets or sets the color used to display empty rating shapes.
@@ -85,8 +85,8 @@ public event EventHandler RatingChanged
/// This uses a non-nullable . A value will be converted to
///
[AllowNull]
- [BindableProperty(PropertyChangedMethodName = nameof(OnRatingColorChanged), CoerceValueMethodName = nameof(CoerceColorToTransparent), DefaultValueCreatorMethodName = nameof(CreateDefaultEmptyShapeColor))]
- public partial Color EmptyShapeColor { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnRatingColorChanged), CoerceValueMethodName = nameof(CoerceColorToTransparent))]
+ public partial Color EmptyShapeColor { get; set; } = RatingViewDefaults.EmptyShapeColor;
///
/// Gets or sets the color used to fill the rating indicator.
@@ -95,22 +95,22 @@ public event EventHandler RatingChanged
/// This uses a non-nullable . A value will be converted to
///
[AllowNull]
- [BindableProperty(PropertyChangedMethodName = nameof(OnRatingColorChanged), CoerceValueMethodName = nameof(CoerceColorToTransparent), DefaultValueCreatorMethodName = nameof(CreateDefaultFillColor))]
- public partial Color FillColor { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnRatingColorChanged), CoerceValueMethodName = nameof(CoerceColorToTransparent))]
+ public partial Color FillColor { get; set; } = RatingViewDefaults.FillColor;
///
/// Gets or sets a value indicating whether the control is in a read-only state.
///
- [BindableProperty(DefaultValue = RatingViewDefaults.IsReadOnly, PropertyChangedMethodName = nameof(OnIsReadOnlyChanged))]
- public partial bool IsReadOnly { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnIsReadOnlyChanged))]
+ public partial bool IsReadOnly { get; set; } = RatingViewDefaults.IsReadOnly;
///
/// Gets or sets the maximum rating value that can be assigned.
///
/// The maximum rating determines the upper limit for rating inputs. Changing this value may affect
/// validation and display of rating controls. The value must be a positive integer.
- [BindableProperty(DefaultValue = RatingViewDefaults.MaximumRating, PropertyChangedMethodName = nameof(OnMaximumRatingChanged), PropertyChangingMethodName = nameof(OnMaximumRatingChanging))]
- public partial int MaximumRating { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnMaximumRatingChanged), PropertyChangingMethodName = nameof(OnMaximumRatingChanging))]
+ public partial int MaximumRating { get; set; } = RatingViewDefaults.MaximumRating;
///
/// Gets or sets the fill behavior to apply when the rating view is tapped.
@@ -118,28 +118,28 @@ public event EventHandler RatingChanged
/// Use this property to control how the rating view visually responds to user interaction. The
/// selected fill option determines the appearance of the rating elements when tapped.
[Obsolete($"Use {nameof(FillOption)} instead")]
- [BindableProperty(DefaultValue = RatingViewDefaults.FillWhenTapped, PropertyChangedMethodName = nameof(OnRatingColorChanged))]
- public partial RatingViewFillOption FillWhenTapped { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnRatingColorChanged))]
+ public partial RatingViewFillOption FillWhenTapped { get; set; } = RatingViewDefaults.FillWhenTapped;
///
/// Gets or sets the rating value for the item.
///
/// The rating must be a valid value as determined by the associated validation method. Changing the
/// rating triggers the property changed callback, which may update related UI or logic.
- [BindableProperty(DefaultValue = RatingViewDefaults.Rating, PropertyChangedMethodName = nameof(OnRatingChanged), PropertyChangingMethodName = nameof(OnRatingChanging))]
- public partial double Rating { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnRatingChanged), PropertyChangingMethodName = nameof(OnRatingChanging))]
+ public partial double Rating { get; set; } = RatingViewDefaults.Rating;
///
/// Gets or sets the amount of space, in device-independent units, between adjacent items.
///
- [BindableProperty(DefaultValue = RatingViewDefaults.Spacing, PropertyChangedMethodName = nameof(OnSpacingChanged))]
- public partial double Spacing { get; set; }
+ [BindableProperty(PropertyChangedMethodName = nameof(OnSpacingChanged))]
+ public partial double Spacing { get; set; } = RatingViewDefaults.Spacing;
/// Gets or sets the element to fill when a is set.
/// Use this property to control how the rating view visually responds to user interaction.
/// The selected fill option determines the appearance of the rating elements when tapped.
- [BindableProperty(DefaultValue = RatingViewDefaults.FillOption, PropertyChangingMethodName = nameof(OnRatingColorChanged))]
- public partial RatingViewFillOption FillOption { get; set; }
+ [BindableProperty(PropertyChangingMethodName = nameof(OnRatingColorChanged))]
+ public partial RatingViewFillOption FillOption { get; set; } = RatingViewDefaults.FillOption;
internal HorizontalStackLayout RatingLayout { get; } = [];
@@ -166,14 +166,6 @@ public event EventHandler RatingChanged
}
};
- static object CreateDefaultShapeBorderColor(BindableObject bindable) => RatingViewDefaults.ShapeBorderColor;
-
- static object CreateDefaultEmptyShapeColor(BindableObject bindable) => RatingViewDefaults.EmptyShapeColor;
-
- static object CreateDefaultFillColor(BindableObject bindable) => RatingViewDefaults.FillColor;
-
- static object CreateDefaultShapePadding(BindableObject bindable) => RatingViewDefaults.ShapePadding;
-
static object CoerceColorToTransparent(BindableObject bindable, object value)
{
var colorValue = (Color?)value;