Summary of Chapter 11. The Bindable infrastructure

Note

This book was published in the spring of 2016, and has not been updated since then. There is much in the book that remains valuable, but some of the material is outdated, and some topics are no longer entirely correct or complete.

Every C# programmer is familiar with C# properties. Properties contain a set accessor and/or a get accessor. They are often called CLR properties for the Common Language Runtime.

Xamarin.Forms defines an enhanced property definition called a bindable property encapsulated by the BindableProperty class and supported by the BindableObject class. These classes are related but quite distinct: The BindableProperty is used to define the property itself; BindableObject is like object in that it is a base class for classes that define bindable properties.

The Xamarin.Forms class hierarchy

The ClassHierarchy sample uses reflection to display a class hierarchy of Xamarin.Forms and demonstrate the crucial role played by BindableObject in this hierarchy. BindableObject derives from Object and is the parent class to Element from which VisualElement derives. This is the parent class to Page and View, which is the parent class to Layout:

Triple screenshot of class hierarchy sharing

A peek into BindableObject and BindableProperty

In the classes that derive from BindableObject many CLR properties are said to be "backed by" bindable properties. For example, the Text property of the Label class is a CLR property, but the Label class also defines a public static read-only field named TextProperty of type BindableProperty.

An application can set or get the Text property of Label normally, or the application can set the Text by calling the SetValue method defined by BindableObject with a Label.TextProperty argument. Similarly, an application can obtain the value of the Text property by calling the GetValue method, again with a Label.TextProperty argument. This is demonstrated by the PropertySettings sample.

Indeed, the Text CLR property is entirely implemented using the SetValue and GetValue methods defined by BindableObject in conjunction with the Label.TextProperty static property.

BindableObject and BindableProperty provide support for:

  • Giving properties default values
  • Storing their current values
  • Providing mechanisms for validating property values
  • Maintaining consistency among related properties in a single class
  • Responding to property changes
  • Triggering notifications when a property is about to change or has changed
  • Supporting data binding
  • Supporting styles
  • Supporting dynamic resources

Whenever a property that is backed by a bindable property changes, BindableObject fires a PropertyChanged event identifying the property that has changed. This event is not fired when the property is set to the same value.

Some properties are not backed by bindable properties, and some Xamarin.Forms classes — such as Span — do not derive from BindableObject. Only a class that derives from BindableObject can support bindable properties because BindableObject defines the SetValue and GetValue methods.

Because Span does not derive from BindableObject, none of its properties — such as Text — are backed by a bindable property. This is why a DynamicResource setting on the Text property of Span raises an exception in the DynamicVsStatic sample in the previous chapter. The DynamicVsStaticCode sample demonstrates how to set a dynamic resources in code using the SetDynamicResource method defined by Element. The first argument is an object of type BindableProperty.

Similarly, the SetBinding method defined by BindableObject has a first argument of type BindableProperty.

Defining bindable properties

You can define your own bindable properties using the static BindableProperty.Create method to create a static read-only field of type BindableProperty.

This is demonstrated in the AltLabel class in the Xamarin.FormsBook.Toolkit library. The class derives from Label and lets you specify a font size in points. It is demonstrated in the PointSizedText sample.

Four arguments of the BindableProperty.Create method are required:

  • propertyName: the text name of the property (the same as the CLR property name)
  • returnType: the type of the CLR property
  • declaringType: the type of the class declaring the property
  • defaultValue: the property's default value

Because defaultValue is of type object, the compiler must be able to determine the default value's type. For example, if the returnType is double, the defaultValue should be set to something like 0.0 rather than just 0, or the type mismatch will trigger an exception at runtime.

It is also very common for a bindable property to include:

  • propertyChanged: a static method called when the property changes value. The first argument is the instance of the class whose property has been changed.

The other arguments to BindableProperty.Create are not as common:

  • defaultBindingMode: used in connection with data binding (as discussed in Chapter 16. Data binding)
  • validateValue: a callback to check for a valid value
  • propertyChanging: a callback to indicate when the property is about to change
  • coerceValue: a callback to coerce a set value to another value
  • defaultValueCreate: a callback to create a default value that cannot be shared among instances of the class (for example, a collection)

The read-only bindable property

A bindable property can be read-only. Creating a read-only bindable property requires calling the static method BindableProperty.CreateReadOnly to define a private static read-only field of type BindablePropertyKey.

Then, define the CLR property set accesor as private to call a SetValue overload with the BindablePropertyKey object. This prevents the property from being set outside the class.

This is demonstrated in the CountedLabel class used in the BaskervillesCount sample.