CommunityToolkit / dotnet

.NET Community Toolkit is a collection of helpers and APIs that work for all .NET developers and are agnostic of any specific UI platform. The toolkit is maintained and published by Microsoft, and part of the .NET Foundation.
https://docs.microsoft.com/dotnet/communitytoolkit/?WT.mc_id=dotnet-0000-bramin
Other
3.05k stars 298 forks source link

Enable other classes that already implement the `INotifyPropertyChanged` to leverage Source Generator Attributes #620

Open hawkerm opened 1 year ago

hawkerm commented 1 year ago

Overview

For instance, I already have a class I'm using which inherits from INotifyPropertyChanged. In my scenario, it's ObservableCollection, so I can't modify it as it's in the BCL.

public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged

It already has an OnPropertyChanged helper and PropertyChanged event:

        protected virtual event PropertyChangedEventHandler? PropertyChanged;

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            PropertyChanged?.Invoke(this, e);
        }

It would be great if I could still use some or all of the MVVM attributes for source generators like [ObservableProperty] when inheriting from these classes in my own class.

API breakdown

N/A uses existing attributes.

Usage example

For instance, I have a class already inheriting from this:

public partial class MyClass : ObservableCollection<T>
{
     // Since my parent class already inherits from INotifyPropertyChanged (and already has a OnPropertyChanged method), just use those...
     [ObservableProperty]
     private bool _isModified;
}

Worst case, let me have to re-specify that I want whatever's needed to polyfill, either with INotifyPropertyChanged or ObservableObject:

[INotifyPropertyChanged]
public partial class MyClass : ObservableCollection<T>
{
     [ObservableProperty]
     private bool _isModified;

     partial OnIsModifiedChanged(bool value)
     {
         // Yay, I could also do this now...
     }
}

Basically the existing generator in this case would spit out all the same stuff except what's already implemented by the type (in the case of ObservableCollection it'd be the following:

    partial class MyClass : global::System.ComponentModel.INotifyPropertyChanged
    {
        //// The Event and the event args OnPropertyChanged are already implemented, so skip adding them

        /// <summary>
        /// Raises the <see cref = "PropertyChanged"/> event.
        /// </summary>
        /// <param name = "propertyName">(optional) The name of the property that changed.</param>
        [global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "8.1.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCode]
        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
        protected void OnPropertyChanged([global::System.Runtime.CompilerServices.CallerMemberName] string? propertyName = null)
        {
            OnPropertyChanged(new global::System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }

        //// All the SetProperty helpers, TaskNotifier, etc...

If there's any conflict for some particular type, then raise an analytic warning/error.

Breaking change?

No

Alternatives

Can't use MVVM Toolkit generators or helpers? 😥

Additional context

Don't know if I know enough to help here. Had a hard time trying to understand how the existing code was generated with the new fanciness...

Help us help you

Yes, if someone can help (I'm not sure I understand where the current INotifyPropertyChanged generator is and how it works. Nor do I know if I know how to detect if something inherits or has the required methods to skip outputting them... but beyond that, I'd be willing to assist giving it a go if pointed in the right directions.

eeevans commented 1 year ago

It would be nice for integrating with Caliburn.Micro base classes for screens and conductors.

varyamereon commented 1 year ago

I have a similar use case for writing custom controls in Maui, they inherit already from 'ContentView' which implements INotifyPropertyChanged. Have some properties that I would like to add an ObservableProperty attribute to but it's not currently possible.