Skip to content
This repository has been archived by the owner on Dec 19, 2022. It is now read-only.

Turns your auto properties into Xamarin.Forms BindableProperties.

License

Notifications You must be signed in to change notification settings

PascalEschbach/XF.BindableProperty

Repository files navigation

XF.BindableProperty.Fody

Build Status NuGet Status

This is an add-in for Fody.

Turns your auto properties into Xamarin.Forms BindableProperties.

Usage

See also Fody usage.

NuGet installation

Install the XF.BindableProperty.Fody NuGet package and update the Fody NuGet package:

PM> Install-Package Fody
PM> Install-Package XF.BindableProperty.Fody

The Install-Package Fody is required since NuGet always defaults to the oldest, and most buggy, version of any dependency.

Add to FodyWeavers.xml

Add <XF.BindableProperty /> to FodyWeavers.xml

<Weavers>
  <XF.BindableProperty />
</Weavers>

Overview

What you write:

public class Foo : BindableObject
{
    [Bindable]
    public string Bar { get; set; }

    [Bindable( BindingMode = XFBindingMode.OneTime, OwningType = typeof(Color))]
    public string Baz { get; set; } = "abc123";
    
    [Bindable]
    public string ReadonlyBar { get; } = "abc123";
    
    [Bindable]
    public string ReadonlyBaz { get; private set; }
}

What gets compiled:

public class Foo : BindableObject
{
    public static readonly BindableProperty BarProperty = BindableProperty.Create(nameof(Bar), typeof(string), typeof(Foo), default(string), BindingMode.OneWay);
    public string Bar {
        get => (string)GetValue(BarProperty);
        set => SetValue(BarProperty, value);
    }

    public static readonly BindableProperty BazProperty = BindableProperty.Create(nameof(Baz), typeof(string), typeof(Color), "abc123", BindingMode.OneTime);
    public string Baz {
        get => (string)GetValue(BazProperty);
        set => SetValue(BazProperty, value);
    }
    
    private static readonly BindablePropertyKey ReadonlyBarPropertyKey = BindableProperty.CreateReadOnly(nameof(ReadonlyBar), typeof(string), typeof(Foo), "abc123", BindingMode.OneWay);
    public static readonly BindableProperty ReadonlyBarProperty = ReadonlyBarPropertyKey.BindableProperty;
    public string ReadonlyBar {
        get => (string)GetValue(ReadonlyBarProperty);
    }
    
    private static readonly BindablePropertyKey ReadonlyBazPropertyKey = BindableProperty.CreateReadOnly(nameof(ReadonlyBaz), typeof(string), typeof(Foo), default(string), BindingMode.OneWay);
    public static readonly BindableProperty ReadonlyBazProperty = ReadonlyBazPropertyKey.BindableProperty;
    public string ReadonlyBaz {
        get => (string)GetValue(ReadonlyBazProperty);
        private set => SetValue(ReadonlyBazPropertyKey, value);
    }
}

Configuration

XF.BindableProperties is highly customizable. Every option which you could normally specify on the BindableProperty.Create method is either implicitly or explicitly configureable.

Callbacks

BindableProperty.Create supports five callbacks.

  • OnPropertyChanged
  • OnPropertyChanging
  • OnCoerceValue
  • OnValidateValue
  • OnCreateDefaultValue

All those callbacks can be implicitly specified in your code:

public class Foo : BindableObject
{
    [Bindable]
    public string Bar { get; set; }

    private static void OnBarChanged( BindableObject bindable, object oldValue, object newValue ) => throw new NotImplementedException();
    private static void OnBarChanging( BindableObject bindable, object oldValue, object newValue ) => throw new NotImplementedException();
    private static object OnCoerceBarValue( BindableObject bindable, object value ) => throw new NotImplementedException();
    private static bool OnValidateBarValue( BindableObject bindable, object value ) => throw new NotImplementedException();
    private static object OnCreateBarValue( BindableObject bindable ) => throw new NotImplementedException();
}
  • The callbacks are automatically looked up. The pattern has to be exact, simply replace 'Bar' with your property name.
  • Make sure the method signature and return types exactly match!
  • You can specify any of those callbacks or none at all, they aren't required.

Furthermore, callbacks can be explicitly specified within the attribute:

public class Foo : BindableObject
{
    [Bindable(
        OnPropertyChanged = nameof(PropertyChangedMethod),
        OnPropertyChanging= nameof(PropertyChangingMethod),
        OnCoerceValue = nameof(CoerceValueMethod),
        OnValidateValue = nameof(ValidateValueMethod),
        OnCreateValue = nameof(CreateValueMethod)
    )]
    public string Bar { get; set; }

    private static void PropertyChangedMethod( BindableObject bindable, object oldValue, object newValue ) => throw new NotImplementedException();
    private static void PropertyChangingMethod( BindableObject bindable, object oldValue, object newValue ) => throw new NotImplementedException();
    private static object CoerceValueMethod( BindableObject bindable, object value ) => throw new NotImplementedException();
    private static bool ValidateValueMethod( BindableObject bindable, object value ) => throw new NotImplementedException();
    private static object CreateValueMethod( BindableObject bindable ) => throw new NotImplementedException();
}
  • If the method doesn't exist or any other error, such as signature mismatch, is found, the weaver will throw an exception.
  • The method names can be anything.

Default values

Initializing your property with a normal property initializer is enough to instruct the system with the necessary information. The property initializer will automatically be included in the BindableProperty.Create method call. This approach is constrained by normal field/property initializer rules.

If instead you need more fine grained control or instance level access, you can register (or implicitly) use the 'OnCreateValue' callback.

public class Foo : BindableObject
{
    [Bindable]
    public string Default { get; set; } = "abc";
   
    [Bindable( OnCreateValue = nameof(CreateValueMethod))]
    public string Explicit { get; set; }
    private static object CreateValueMethod( BindableObject bindable ) => "abc"; //Instance level access throught 'bindable' argument
    
    [Bindable]
    public string Implicit { get; set; }
    private static object OnCreateImplicitValue( BindableObject bindable ) => "abc"; //Instance level access throught 'bindable' argument
}

Other

  • The binding mode can be controlled via the 'BindingMode' property of the attribute.
  • The concrete owner of the BindableProperty can be controlled via the 'OwningType' property.

Remarks

  • Auto-Properties: Only auto properties are supported. Properties with getter/setter bodies are invalid by definition as Xamarin accesses the BindableProperty directly, not the actual property. As such, custom getter/setter implementations would cause diverging behaviours between XAML and code.
  • OnCoerceValue: Use this callback to constrain inputs instead of a custom getter/setter!
  • OnValidateValue: Use this callback to do any input validation!
  • OnPropertyChanged/OnPropertyChanging: Use those callbacks to notify dependant properties or trigger custom behaviour!
  • OnCreateValue: Use this callback to construct a default value which required instance level access or runtime information!
  • Readonly properties: All properties without a publicly available setter (or none) will automatically be turned into readonly properties following the BindableProperty/BindablePropertyKey pattern.
  • Getters: Properties must have publicly available getters. Private getters, or none at all, arent supported and will throw weaving exceptions!

Roadmap

  • Attached properties
  • Dependant property notification

Icon

Icon designed by Matt Hawdon from The Noun Project.

About

Turns your auto properties into Xamarin.Forms BindableProperties.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Languages