This is an add-in for Fody.
Turns your auto properties into Xamarin.Forms BindableProperties.
See also Fody usage.
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 <XF.BindableProperty />
to FodyWeavers.xml
<Weavers>
<XF.BindableProperty />
</Weavers>
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);
}
}
XF.BindableProperties is highly customizable. Every option which you could normally specify on the BindableProperty.Create method is either implicitly or explicitly configureable.
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.
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
}
- 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.
- 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!
- Attached properties
- Dependant property notification
Icon designed by Matt Hawdon from The Noun Project.