diff --git a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs index efac4276..0e99c852 100644 --- a/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs +++ b/CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/ObservablePropertyGenerator.Execute.cs @@ -91,12 +91,19 @@ internal static class Execute { // Gather dependent property and command names if (TryGatherDependentPropertyChangedNames(fieldSymbol, attributeData, propertyChangedNames, builder) || - TryGatherDependentCommandNames(fieldSymbol, attributeData, notifiedCommandNames, builder) || - TryGetIsBroadcastingChanges(fieldSymbol, attributeData, builder, out alsoBroadcastChange)) + TryGatherDependentCommandNames(fieldSymbol, attributeData, notifiedCommandNames, builder)) { continue; } + // Check whether the property should also broadcast changes + if (TryGetIsBroadcastingChanges(fieldSymbol, attributeData, builder, out bool isBroadcastTargetValid)) + { + alsoBroadcastChange = isBroadcastTargetValid; + + continue; + } + // Track the current validation attribute, if applicable if (attributeData.AttributeClass?.InheritsFromFullyQualifiedName("global::System.ComponentModel.DataAnnotations.ValidationAttribute") == true) { @@ -332,13 +339,13 @@ bool IsCommandNameValidWithGeneratedMembers(string commandName) /// The input instance to process. /// The instance for . /// The current collection of gathered diagnostics. - /// Whether or not the resulting property should also broadcast changes. + /// Whether or not the the property is in a valid target that can broadcast changes. /// Whether or not the generated property for used [AlsoBroadcastChange]. private static bool TryGetIsBroadcastingChanges( IFieldSymbol fieldSymbol, AttributeData attributeData, ImmutableArray.Builder diagnostics, - out bool alsoBroadcastChange) + out bool isBroadcastTargetValid) { if (attributeData.AttributeClass?.HasFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.AlsoBroadcastChangeAttribute") == true) { @@ -346,7 +353,7 @@ private static bool TryGetIsBroadcastingChanges( if (fieldSymbol.ContainingType.InheritsFromFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipient") || fieldSymbol.ContainingType.HasOrInheritsAttributeWithFullyQualifiedName("global::CommunityToolkit.Mvvm.ComponentModel.ObservableRecipientAttribute")) { - alsoBroadcastChange = true; + isBroadcastTargetValid = true; return true; } @@ -358,12 +365,12 @@ private static bool TryGetIsBroadcastingChanges( fieldSymbol.ContainingType, fieldSymbol.Name); - alsoBroadcastChange = false; + isBroadcastTargetValid = false; return true; } - alsoBroadcastChange = false; + isBroadcastTargetValid = false; return false; } diff --git a/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs b/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs index ec7d1a93..ea0db7b4 100644 --- a/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs +++ b/tests/CommunityToolkit.Mvvm.UnitTests/Test_ObservablePropertyAttribute.cs @@ -608,6 +608,43 @@ public void Test_ObservableProperty_ModelWithCultureAwarePropertyName() CollectionAssert.AreEqual(new[] { nameof(model.InputFolder) }, propertyNames); } + // See https://github.com/CommunityToolkit/dotnet/issues/242 + [TestMethod] + public void Test_ObservableProperty_ModelWithAlsoBroadcastChangeAndDisplayAttributeLast() + { + IMessenger messenger = new StrongReferenceMessenger(); + ModelWithAlsoBroadcastChangeAndDisplayAttributeLast model = new(messenger); + + List propertyNames = new(); + + model.PropertyChanged += (s, e) => propertyNames.Add(e.PropertyName); + + object newValue = new(); + bool isMessageReceived = false; + + messenger.Register>(this, (r, m) => + { + if (m.Sender != model) + { + Assert.Fail(); + } + + if (m.NewValue != newValue) + { + Assert.Fail(); + } + + isMessageReceived = true; + }); + + model.SomeProperty = newValue; + + Assert.AreEqual(model.SomeProperty, newValue); + Assert.IsTrue(isMessageReceived); + + CollectionAssert.AreEqual(new[] { nameof(model.SomeProperty) }, propertyNames); + } + public abstract partial class BaseViewModel : ObservableObject { public string? Content { get; set; } @@ -991,4 +1028,13 @@ partial class ModelWithCultureAwarePropertyName [ObservableProperty] private int _inputFolder; } + + [ObservableRecipient] + public sealed partial class ModelWithAlsoBroadcastChangeAndDisplayAttributeLast : ObservableValidator + { + [ObservableProperty] + [AlsoBroadcastChange] + [Display(Name = "Foo bar baz")] + private object? _someProperty; + } }