diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs
index c6bf051a..1e04959b 100644
--- a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs
+++ b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs
@@ -38,12 +38,14 @@ internal static class Execute
/// The cancellation token for the current operation.
/// The resulting value, if successfully retrieved.
/// The resulting diagnostics from the processing operation.
+ /// Generated property name. If empty, the property name will be generated from the field name.
/// The resulting instance for , if successful.
public static bool TryGetInfo(
FieldDeclarationSyntax fieldSyntax,
IFieldSymbol fieldSymbol,
SemanticModel semanticModel,
CancellationToken token,
+ string PropertyName,
[NotNullWhen(true)] out PropertyInfo? propertyInfo,
out ImmutableArray diagnostics)
{
@@ -69,7 +71,8 @@ public static bool TryGetInfo(
// Get the property type and name
string typeNameWithNullabilityAnnotations = fieldSymbol.Type.GetFullyQualifiedNameWithNullabilityAnnotations();
string fieldName = fieldSymbol.Name;
- string propertyName = GetGeneratedPropertyName(fieldSymbol);
+ // Generate if not specified
+ string propertyName = string.IsNullOrEmpty(PropertyName) ? GetGeneratedPropertyName(fieldSymbol) : PropertyName;
// Check for name collisions
if (fieldName == propertyName)
diff --git a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs
index ad7e5fe0..749aec6f 100644
--- a/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs
+++ b/src/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.cs
@@ -44,7 +44,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
token.ThrowIfCancellationRequested();
- _ = Execute.TryGetInfo(fieldDeclaration, fieldSymbol, context.SemanticModel, token, out PropertyInfo? propertyInfo, out ImmutableArray diagnostics);
+ string propertyName = context.Attributes.FirstOrDefault()?.GetConstructorArguments().FirstOrDefault() ?? string.Empty;;
+
+ _ = Execute.TryGetInfo(fieldDeclaration, fieldSymbol, context.SemanticModel, token, propertyName, out PropertyInfo? propertyInfo, out ImmutableArray diagnostics);
token.ThrowIfCancellationRequested();
diff --git a/src/CommunityToolkit.Mvvm/ComponentModel/Attributes/ObservablePropertyAttribute.cs b/src/CommunityToolkit.Mvvm/ComponentModel/Attributes/ObservablePropertyAttribute.cs
index 0e765267..8a91b152 100644
--- a/src/CommunityToolkit.Mvvm/ComponentModel/Attributes/ObservablePropertyAttribute.cs
+++ b/src/CommunityToolkit.Mvvm/ComponentModel/Attributes/ObservablePropertyAttribute.cs
@@ -49,9 +49,24 @@ namespace CommunityToolkit.Mvvm.ComponentModel;
/// The generated properties will automatically use the UpperCamelCase format for their names,
/// which will be derived from the field names. The generator can also recognize fields using either
/// the _lowerCamel or m_lowerCamel naming scheme. Otherwise, the first character in the
-/// source field name will be converted to uppercase (eg. isEnabled to IsEnabled).
+/// source field name will be converted to uppercase (eg. isEnabled to IsEnabled). For
+/// custom property names use the
///
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public sealed class ObservablePropertyAttribute : Attribute
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ObservablePropertyAttribute()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Generated property name.
+ public ObservablePropertyAttribute(string name)
+ {
+ }
}
diff --git a/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs b/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs
index 495a9cd5..d1b0dc9f 100644
--- a/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs
+++ b/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs
@@ -1101,6 +1101,22 @@ public void Test_ObservableProperty_ForwardedAttributesWithNegativeValues()
.Value);
}
+ // See https://github.com/CommunityToolkit/dotnet/issues/795
+ [TestMethod]
+ public void Test_ObservableProperty_ModelWithPropertyNames()
+ {
+ ModelWithPropertyNames model = new();
+
+ List propertyNames = new();
+
+ model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName);
+
+ CollectionAssert.AreEqual(new[]
+ {
+ nameof(model.fieldProp),
+ }, propertyNames);
+ }
+
public abstract partial class BaseViewModel : ObservableObject
{
public string? Content { get; set; }
@@ -1797,4 +1813,11 @@ private sealed partial class ModelWithDependentPropertyAndNoPropertyChanging
public string? FullName => "";
}
+
+ public partial class ModelWithPropertyNames : ObservableObject
+ {
+ // Inherited property
+ [ObservableProperty("fieldProp")]
+ private string? m_field;
+ }
}