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
:
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 propertydeclaringType
: the type of the class declaring the propertydefaultValue
: 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 valuepropertyChanging
: a callback to indicate when the property is about to changecoerceValue
: a callback to coerce a set value to another valuedefaultValueCreate
: 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.