question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Proposal] BindableProperty generator

See original GitHub issue

[Bindable Property (BP) generator]

  • Proposed
  • Prototype: Not Started
  • Implementation: Not Started
    • iOS Support
    • Android Support
    • macOS Support
    • Windows Support
  • Unit Tests: Not Started
  • Sample: Not Started
  • Documentation: Not Started

Link to Discussion

Summary

This feature will make our lives easier (and in the future the lives of our users) allowing us to create BindableProperties without having to type too much (I hope).

Important, this just covers the BindableProperty.Create method!

This does not cover the BindableProperty.CreateAttached

Motivation

We are lazy devs that don’t like to write boilerplate code, so the SG is here to save us! Let’s use it

Detailed Design

Right now we don’t have a final decision on what the API should look like, and it’s fine so far, so I’ll post here some proposals that we can vote on. Maybe we can have more than one implemented in order to make the class readable (avoid a lot of attributes at the class level for example). It’s very easy to implement an attribute that covers all parameters from the BindableProperty.Create method, so we can do that in our v1.

1. Using attributes at the class level

 [BindableProperty(PropertyName = "CardTitle", ReturnType = typeof(string), OwnerType = typeof(CardView), DefaultValue = string.Empty, PropertyChangedMethodName = "Invalidate")]
 [BindableProperty(PropertyName = "CardDescription", ReturnType = typeof(string)]
public partial class CardView : ContentView 
{
       public static void Invalidate() { }  // OnPropertyChanged method
}

// SG will generate
public partial class CardView
{
      public static readonly BindableProperty CardTitleProperty = BindableProperty.Create("CardTitle", typeof(string), typeof(CardView), string.Empty, propertyChaged: Invalidate);
    public string CardTitle
    {
         get => (string)GetValue(CardTitleProperty);
         set => SetValue(CardTitleProperty, value);
    }

     public static readonly BindableProperty CardDescriptionProperty = BindableProperty.Create("CardDescription", typeof(string), typeof(CardView));

    public string CardDescription
    {
         get => (string)GetValue(CardDescriptionProperty);
         set => SetValue(CardDescriptionProperty, value);
    }
}

The CardTitle shows how will be a more complete implementation if the user needs it and the CardDescription shows how will a simple implementation when there’s no need to specify all the parameters. Also, in the CardDescription we don’t need to provide the OwnerType, the SG can infer that will the class.

2. Using attributes at the ctor level

public partial class CardView : ContentView 
{

       [BindableProperty(PropertyName = "CardTitle", ReturnType = typeof(string), OwnerType = typeof(CardView), DefaultValue = string.Empty, PropertyChangedMethodName = "Invalidate")]
       [BindableProperty(PropertyName = "CardDescription", ReturnType = typeof(string)]
       public CardView() { }

       public static void Invalidate() { }  // OnPropertyChanged method
}

// SG will generate
public partial class CardView
{
      public static readonly BindableProperty CardTitleProperty = BindableProperty.Create("CardTitle", typeof(string), typeof(CardView), string.Empty, propertyChaged: Invalidate);
    public string CardTitle
    {
         get => (string)GetValue(CardTitleProperty);
         set => SetValue(CardTitleProperty, value);
    }

     public static readonly BindableProperty CardDescriptionProperty = BindableProperty.Create("CardDescription", typeof(string), typeof(CardView));

    public string CardDescription
    {
         get => (string)GetValue(CardDescriptionProperty);
         set => SetValue(CardDescriptionProperty, value);
    }
}

The same as the 1 but now we decorate the ctor. If we think this will look better But this can be used in a method as well, like if the user creates the propertyChanged method it can be used to create the BP using the attribute.

3 Using the Property

[BindablePropertyGenerator]
public partial class CardView : ContentView 
{
    [BindableProperty(PropertyName = "CustomName", OwnerType = typeof(CardView), DefaultValue = string.Empty, PropertyChangedMethodName = "Invalidate")]
    public string CardTitle
    {
         get => (string)GetValue(CustomNameProperty);
         set => SetValue(CustomNameProperty, value);
    }
    public static void Invalidate() { }  // OnPropertyChanged method

    [BindableProperty]
    public string CardDescription
    {
         get => (string)GetValue(CardDescriptionProperty);
         set => SetValue(CardDescriptionProperty, value);
    }
}

// SG will generate
public partial class CardView
{
      public static readonly BindableProperty CustomNameProperty = BindableProperty.Create("CardTitle", typeof(string), typeof(CardView), string.Empty, propertyChaged: Invalidate);

     public static readonly BindableProperty CardDescriptionProperty = BindableProperty.Create("CardDescription", typeof(string), typeof(CardView));

}

Here we can just mark a global attribute to SG lookup and then we use the properties to hold the attribute. With this, we can infer the return type and the Bindable Property name. The user will need to implement the getter and setter, but if this is in the language some day we can implement the getter and setter.

4 Using the BindableProperty field

//User code

public partial class CardView  : ContentView 
{
     [BindableProperty(PropertyName = "CardTitle", ReturnType = typeof(string), OwnerType = typeof(CardView), DefaultValue = string.Empty, PropertyChangedMethodName = "Invalidate")]
    public static readonly BindableProperty CardTitleProperty;

    partial void InitializeStatic()
    {
       lbl.GesturesRecognizer.Add(new GestureRecognizer());
    }

    public static void Invalidate() { }  // OnPropertyChanged method
}

// SG will generate

public partial class CardView
{
    static CardView()
    {
        CardTitleProperty = BindableProperty.Create("CardTitle", typeof(string), typeof(CardView), string.Empty, propertyChaged: Invalidate);
        InitializeStatic();
    }

    static partial void InitializeStatic();

    public string CardDescription
    {
         get => (string)GetValue(CardDescriptionProperty);
         set => SetValue(CardDescriptionProperty, value);
    }

}

Here we will use the BindableProperty field to hold the attribute and generate the rest of the code.

Usage Syntax

I’ll fill this out when we define an API design.

Drawbacks

When C#11 arrives with generic in attributes we can make it cleaner, so will be breaking changes in the future (IMHO we should do breaking change instead of maintaining one more pattern to declare the BP, but this is a discussion for the future).

the docs say that will be in C#10 but it was changed.

Alternatives

None.

Unresolved Questions

Issue Analytics

  • State:open
  • Created a year ago
  • Reactions:10
  • Comments:13 (8 by maintainers)

github_iconTop GitHub Comments

4reactions
Sergio0694commented, Aug 16, 2022

FWIW, I plan to implement this (for DependencyProperty) for UWP/WinUI 3, but I’ll wait for C# 12 and hopefully partial properties. While other approaches would also work, I don’t personally like them (especially using fields over a class, with the property name as a string), as they’re just not idiomatic enough. You might want to consider when you plan to ship this generator as a stable release. If it’s something that might be done in like a couple months, it’s one thing, but if this is something that could reach a stable release in a year, then it might just be worth it to wait for C# 12 directly and do that in a consistent way with the Windows Community Toolkit as well. Just my two cents here 🙂

As in:

[DependencyProperty(...)]
public partial string Name { get; set; }

If/when we do get partial properties I also plan to switch to them for the MVVM Toolkit, and eventually deprecate annotating fields. That’s really just a temporary workaround for now, and I don’t really like it. It’s just the best we could do for now. If this is something the MAUI Community Toolkit team was interested in, we could use this as another use case scenario in the C# language proposal we’re working on, to try to get more support for this.

Either way, I’m available in case you have any source generator questions you think I could help with 😄

1reaction
pictoscommented, Aug 16, 2022

@brminnick but the field will not be of string or any other type but BindableProperty

Read more comments on GitHub >

github_iconTop Results From Across the Web

rrmanzano/maui-bindableproperty-generator
Generator. Source generator that automatically transforms fields into BindableProperties that can be used in MAUI. Are you looking for the Xamarin project?
Read more >
rrmanzano/xamarin-bindableproperty-generator
Generator. Source generator that automatically transforms fields into BindableProperties that can be used in Xamarin. Are you looking for the MAUI project?
Read more >
Bindable Properties - .NET MAUI
NET MAUI bindable properties provide a property system that supports data binding, styles, tempaltes, and values set through parent-child ...
Read more >
Two-way data binding
Two-way data binding using custom attributes · Annotate the method that sets the initial value and updates when the value changes using @ ......
Read more >
All about property bindings in Qt 6.2
This blog gives an update on property bindings in Qt 6.2 and demonstrates how bindable properties can simplify C++ code.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found