Skip to content

Add support for [ObservableProperty] on partial properties #555

Closed

Description

@Sergio0694

Overview

This issue tracks adding support for partial properties to the [ObservableProperty] generator. This is blocked on dotnet/csharplang#6420, so support for this will have to wait for that first (hopefully in the C# 12 timeframe).

API breakdown

The changes are pretty straightforward: properties will also be allowed on [ObservableProperty]:

-[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
+[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
 public sealed class ObservablePropertyAttribute : Attribute
 {
 }

Analyzers will also need to be added to ensure that the annotated properties are partial and valid.

Usage example

Code like this:

[ObservableProperty] 
public partial string Name { get; private set; }

Will generate the following:

/// <inheritdoc cref="_itemsSource"/>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
public partial string Name
{
    get => field;
    set
    {
        if (!global::System.Collections.Generic.EqualityComparer<string>.Default.Equals(field, value))
        {
            OnNameChanging(value);
            OnPropertyChanging(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangingArgs.Name);
            
            field = value;

            OnItemsSourceChanged(value);
            OnNameChanged(global::CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.Name);
        }
    }
}

/// <summary>Executes the logic for when <see cref="Name"/> is changing.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")]
partial void OnNameChanging(string value);

/// <summary>Executes the logic for when <see cref="Name"/> just changed.</summary>
[global::System.CodeDom.Compiler.GeneratedCode("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "8.1.0.0")]
partial void OnNameChanged(string value);

This will also enable support for declaring custom accessibility modifiers for property accessors.

Progress tracking

  • Initial code generation support
  • Add analyzer for C# preview
  • Add analyzer to use partial properties instead of fields
  • Add code fixer to convert fields to partial properties
  • Add analyzer for partial property declarations
  • Add analyzer for unsupported Roslyn version for properties
  • Update all titles/descriptions of existing shared diagnostics

Activity

  1. pinned this issue on Jan 14, 2023
  2. moved this to 📋 Backlog in 9.0on Jan 22, 2023
  3. cmiles commented on May 18, 2023

    @cmiles

    @Sergio0694 I haven't tried to track a language proposal before and couldn't immediately tell from the Partial Properties proposal/labels/information whether it is still expected to be available in the November 2023 time frame that you mentioned in #291 - since you have been tracking and adding to the the Partial Properties proposal/discussion any chance you can add any current information about time frame estimates here?

    Fwiw From what I've read here and in other issues Partial Properties seem like a great solution - just wondering if it is truly 'coming soon' or if this pushes into 2024 and beyond.

    Thanks for your time and any update!

  4. Sergio0694 commented on May 20, 2023

    @Sergio0694
    MemberAuthor

    Hey @cmiles! We don't have any updates/ETAs to share at this time. Keep in mind that no C# features at all are confirmed until the moment they actually ship, so the same applies here. We're still actively looking into this though, just no news just yet 🙂

  5. modmynitro commented on Oct 15, 2023

    @modmynitro
  6. cmiles commented on Oct 21, 2023

    @cmiles

    @Sergio0694 since this did not make it into C#12 any chance that of any of the associated issues will be reconsidered for action in a more immediate time frame?

    I understand partial properties would facilitate the ideal implementation of things like 'Required' keyword support - but there is some functionality blocked by this that would be immediately useful and appreciated even if there were breaking changes in the future if/when partial properties make it into C#.

  7. Sergio0694 commented on Oct 22, 2023

    @Sergio0694
    MemberAuthor

    "but there is some functionality blocked by this"

    What functionality is blocked?

  8. cmiles commented on Oct 22, 2023

    @cmiles

    What functionality is blocked?

    @Sergio0694 - It was my understanding that all of the issues/functionality listed below are blocked by wanting to wait and create an implementation based around the partial property language feature.

    For me the functionality the required keyword offers is particularly attractive as we work on enabling nullable support in our code - but I'm not aware of any way to combine [ObservableProperty] and required?

    Based on your question of 'What functionality is blocked?' maybe I am mistaken and there is a feature or approach that I'm not aware of to use [ObservableProperty] and required together - if so apologies for the wasted time/space in this issue but would very much appreciate a link to get me started!

    Add support for "required" and "virtual" keywords to property generator · Issue #679 · CommunityToolkit/dotnet
    Make ObservableProperty Generated Code Properties Virtual? · Issue #543 · CommunityToolkit/dotnet
    Add support for setter protection level for ObservableProperty · Issue #758 · CommunityToolkit/dotnet
    Add abilities to control the access level of generated [ObservableProperty] attribute · Issue #585 · CommunityToolkit/dotnet

  9. Sergio0694 commented on Oct 22, 2023

    @Sergio0694
    MemberAuthor

    "all of the issues/functionality listed below are blocked"

    "For me the functionality the required keyword offers is particularly attractive"

    No functionality is actually blocked. You can easily use all that — just manually define the properties 🙂

    I understand the frustration for wanting a simpler way to do that, and I do agree that of course using the generator is better. But I don't think it's a good idea to dedicate resources to implement all that functionality when we might very well have partial properties out in preview soon, especially because then we'd still have to keep supporting that "workaround" to avoid breaking consumers. Rather, it makes more sense to just wait for partial properties to be available in preview, add support for those, and then release a preview of the MVVM Toolkit too so that people wanting to try those out can do so as well.

  10. cmiles commented on Oct 23, 2023

    @cmiles

    @Sergio0694 No frustration here - seems like we just have a different evaluation of the issue.

    Fwiw I would still '+1' working on a solution now - but respect and understand your reasoning and decision and no doubt you have 100% better insight on the odds of partial properties arriving in a near future preview.

    I hope this issue will continue to be re-evaluated if for some reason partial properties, or something equivalently useful for generators, don't arrive for the next version of C#.

    Thanks for the prompt replies - appreciated!

  11. jhm-ciberman commented on Dec 16, 2023

    @jhm-ciberman

    This was not added to C# 12. Is there any reason to not implement the other more traditional alternatives using attributes?

  12. Sergio0694 commented on Dec 16, 2023

    @Sergio0694
    MemberAuthor

    This exact question and the answer for that are in the messages right above yours — the reason has not changed and it's the same as before 🙂

  13. 39 remaining items

  14. jphorv-bdo commented on Jan 21, 2025

    @jphorv-bdo

    I also have unexpected issues with this partial property feature, and I'm unsure I'll be able to use it nearly as much as I'd hoped. Notifications being sent while still in the constructor causes a lot of issues in my codebase. What started with me happily deleting hand-written properties that existed only because I wanted private setters, ended with me stashing the entire changeset.

  15. SirViver commented on Jan 21, 2025

    @SirViver

    Another (ugly/hack) workaround would be to make a SetPropertyWithoutNotification method that directly sets the property backing field via reflection.

    // Custom ObservableObject-derived base class defines this method:
    
    protected void SetPropertyWithoutNotification<T>(T property, T newValue,
        [CallerArgumentExpression(nameof(property))] string propertyName = "")
    {
        if (propertyName.LastIndexOf('.') is int dotIndex and >= 0)
        {
            propertyName = propertyName[(dotIndex + 1)..];
        }
    
        string backingFieldName = $"<{propertyName}>k__BackingField";
        
        var backingField = this.GetType().GetField(backingFieldName, BindingFlags.Instance | BindingFlags.NonPublic) 
            ?? throw new InvalidOperationException($"Backing field '{backingFieldName}' not found for property '{propertyName}'.");
        
        backingField.SetValue(this, newValue);
    }
    
    // Use in derived class like so:
    
    [ObservableProperty]
    public partial int MyProperty { get; set; }
    
    public MyClass()
    {
        SetPropertyWithoutNotification(MyProperty, 42);
    }

    Of course the usual caveats regarding reflection performance and ignoring/circumvention of property access modifiers apply, but... 🤷

  16. Sergio0694 commented on Jan 21, 2025

    @Sergio0694
    MemberAuthor

    @weitzhandler @SirViver we're looking at fixing this in C# 14 🙂
    See: dotnet/csharplang#9031.

  17. AignerGames commented on Jan 22, 2025

    @AignerGames

    The fieldof thing looks weird, I think the easier and more obvious solution would be to give more control the the source generator. Instead of always using the field keyword for the generated property, add a parameter to the property attribute to generate a "real" field with a name. Then you could just write the value to the generated field like before, but still use the new features which are only possible with partial properties.

    Would be a clean and easy solution, without waiting for a new language feature, which is in proposal state, so it's not confirmed that it will be added in C# 14.

  18. weitzhandler commented on Jan 22, 2025

    @weitzhandler

    Another method is adding a IDisposable SuppressChangeNotifications() method in a fashion similar to what we have in the ObservableCollection - calling the method disables notification, disposing its result enables it back on.

    class MyClass : INPC
    {
        [ObservableProperty]
        partial int MyProperty { get; set; }
    
        public MyClass()
        {
            using (SuppressPropertyChangeNotifications())
            {
                MyProperty = 5;
            }
        }
    }

    This is a useful utility method which comes handy regardless of partial properties or initialization.

  19. DennisWelu commented on Jan 22, 2025

    @DennisWelu

    @Sergio0694 I appreciate the entire body of work done in this library it is quite nice. But the philosophy of waiting for the next C# version continues to undermine the usefulness of the ObservableProperty, which may be the most used feature. I am hoping you will reconsider a solution for "today" in the meantime.

  20. JochemPalmsens commented on Jan 23, 2025

    @JochemPalmsens

    @Sergio0694 I appreciate the entire body of work done in this library it is quite nice. But the philosophy of waiting for the next C# version continues to undermine the usefulness of the ObservableProperty, which may be the most used feature. I am hoping you will reconsider a solution for "today" in the meantime.

    IMHO If you think this is a problem, don't use it. Make your own: you can even clone this repo and modify it yourself.
    Or just be glad that they're offering this toolkit at all, which saves us a lot of work and makes code more readible. And that they are actually reading and addressing our issues at al.
    At least, that's how I'm experiencing it. I'm happy with this new feature and glad with the partial property support in .net 9, so we can make stuff required. Thanks guys!

  21. DennisWelu commented on Jan 23, 2025

    @DennisWelu

    @JochemPalmsens I led with a statement of appreciation, which is genuine. And everything you say is valid. But it makes no sense to fork and maintain a large repo every time you disagree with a design or decision. And since this is a community project I assume it is open to community opinions. The history on waiting for a key change in the C# spec on this specific feature, the most valuable feature IMO, goes back years.

  22. AignerGames commented on Jan 24, 2025

    @AignerGames

    Another method is adding a IDisposable SuppressChangeNotifications() method in a fashion similar to what we have in the ObservableCollection - calling the method disables notification, disposing its result enables it back on.

    class MyClass : INPC
    {
    [ObservableProperty]
    partial int MyProperty { get; set; }

    public MyClass()
    {
        using (SuppressPropertyChangeNotifications())
        {
            MyProperty = 5;
        }
    }
    

    }

    This is a useful utility method which comes handy regardless of partial properties or initialization.

    I like this idea and it could even be combined with the other ideas, if desired.

  23. whiskhub commented on Mar 4, 2025

    @whiskhub

    I have had created a ticket for this feature request already: #993

    After discussions I have then created the C# language request, but it seems they will not add it to the language anytime soon as they don't see the justification for a language level fix: dotnet/csharplang#8704 (comment)

    Therefore I'd also really like to see a feature improvement in the source generator. An IDisposable wrapper (#555 (comment)) seems sensible to me.

  24. mbencew commented on Dec 10, 2025

    @mbencew

    Shouldn't we be able to use this feature with C# 14 (instead of preview)? Or do we have to wait for a new version?

  25. Sergio0694 commented on Dec 10, 2025

    @Sergio0694
    MemberAuthor

    Hey folks, the new changes are available starting from version 8.4.1-build.1 in the preview feed (this one). Would you be able to help try it out and let me know if things work as expected now with the MVVM Toolkit generator? Thank you! 🙂

  26. mbencew commented on Dec 11, 2025

    @mbencew

    Hey folks, the new changes are available starting from version 8.4.1-build.1 in the preview feed (this one). Would you be able to help try it out and let me know if things work as expected now with the MVVM Toolkit generator? Thank you! 🙂

    Hello, I added the NuGet package source and modified the .props to include <LangVersion>14.0</LangVersion>, but that is not working, only the preview one.

    The analyzer error with LangVersion 14.0:
    Using [ObservableProperty] on partial properties requires the C# language version to be set to at least 14.0 or 'preview', as support for the 'field' keyword is needed by the source generators to emit valid code (add 14.0 or preview to your .csproj/.props file).

  27. Sergio0694 commented on Dec 11, 2025

    @Sergio0694
    MemberAuthor

    That's a known issue, will be fixed in the next update (already available in the preview feed) 🙂

  28. joehanna commented on Apr 4, 2026

    @joehanna

    Hey @Sergio0694 - I arrived here from your comment on Issue #835 which says that issue is superseded by this one. I am not clear how, but I didn't want to create yet another issue and I am not certain this comment belongs in this one. All of the Issues on my topic are linked forward to this one. I apologise if I have done this incorrectly.

    I am still interested to know how to solve the need to override the generated public property or at least where I can vote for it.

    We don't use lowerCamelCase for our private fields so the ObservableProperty does not get generated as expected. Given coding style is personal and configurable using .editorconfig and most modern IDEs and Code Editors respect it, it makes it difficult consuming this library feature that is strictly opinionated on coding style. I am not expecting it to automatically support multiple styles, but having the ability to override the generated property name would solve that problem for everyone.

    For example (as inspired by the example on the also-linked Issue #795:

    [ObservableProperty("SpecialValue")]
    private double value_using_WhatEVER_coding_style_I_makeup_oR_want;
    

    For the record, we use lower_snake_case for private fields.

    Would you kindly re-consider support for the public property name as an attribute? It is clean, readable, doesn't require tracking of partial properties and makes this feature accessible to us - otherwise we are off the proverbial cliff and back to full boilerplate property declarations :)

    Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature request 📬A request for new changes to improve functionalitymvvm-toolkit 🧰Issues/PRs for the MVVM Toolkit

    Type

    No type

    Projects

    • Status
      ✅ Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions