Summary of Chapter 26. Custom layouts
Xamarin.Forms includes several classes derived from
This chapter describes how to create your own classes that derive from
An overview of layout
There is no centralized system that handles Xamarin.Forms layout. Each element is responsible for determining what its own size should be, and how to render itself within a particular area.
Parents and children
Every element that has children is responsible for positioning those children within itself. It is the parent that ultimately determines what size its children should be based on the size it has available and the size the child wants to be.
Sizing and positioning
Layout begins at the top of the visual tree with the page and then proceeds through all the branches. The most important public method in layout is
Layout defined by
VisualElement. Every element that is a parent to other elements calls
Layout for each of its children to give the child a size and postition relative to itself in the form of a
Rectangle value. These
Layout calls propagate through the visual tree.
A call to
Layout is required for an element to appear on the screen, and causes the following read-only properties to be set. They are consistent with the
Rectangle passed to the method:
Prior to the
Width have mock values of –1.
A call to
Layout also triggers calls to the following protected methods:
Finally, the following event is fired:
OnSizeAllocated method is overridden by
Layout, which are the only two classes in Xamarin.Forms that can have children. The overridden method calls
Layoutderivatives, which calls
LayoutChildren then calls
Layout for each of the element's children. If at least one child has a new
Bounds setting, then the following event is fired:
Constraints and size requests
LayoutChildren to intelligently call
Layout on all its children, it must know a preferred or desired size for the children. Therefore the calls to
Layout for each of the children are generally preceded by calls to
After the book was published, the
GetSizeRequest method was deprecated and replaced with
For many elements,
Measure obtains the native size of the element from its renderer. Both methods have parameters for width and height constraints. For example, a
Label will use the width constraint to determine how to wrap multiple lines of text.
Measure return a value of type
SizeRequest, which has two properties:
Very often these two values are the same, and the
Minimum value can usually be ignored.
VisualElement also defines a protected method similar to
GetSizeRequest that is called from
That method is now deprecated and replaced with:
Every class that derives from
Layout<T> must override
OnMeasure. This is where a layout class determines its own size, which is generally based on the size of its children, which it obtains by calling
Measure on the children. Before and after calling
Measure makes adjustments based on the following properties:
double, affects the
double, affects the
double, affects the
double, affects the
The constraint arguments passed to
OnMeasure) can be infinite (i.e., values of
Double.PositiveInfinity). However, the
SizeRequest returned from these methods cannot contain infinite dimensions.
Infinite constraints indicate that the requested size should reflect the element's natural size. A vertical
Measure) on its children with an infinite height constraint. A horizontal stack layout calls
Measure) on its children with an infinite width constraint. An
Measure) on its children with infinite width and height constraints.
Peeking inside the process
The ExploreChildSize displays constraint and size request information for a simple layout.
Deriving from Layout
A custom layout class derives from
Layout<View>. It has two responsibilities:
Measureon all the layout's children. Return a requested size for the layout itself
Layouton all the layout's children
foreach loop in these overrides should skip any child whose
IsVisible property is set to
A call to
OnMeasure is not guaranteed.
OnMeasure will not be called if the parent of the layout is governing the layout's size (for example, a layout that fills a page). For this reason,
LayoutChildren cannot rely on child sizes obtained during the
OnMeasure call. Very often,
LayoutChildren must itself call
Measure on the layout's children, or you can implement some kind of size caching logic (to be discussed later).
An easy example
Vertical and horizontal positioning simplified
One of the jobs that
VerticalStack must perform occurs during the
LayoutChildren override. The method uses the child's
HorizontalOptions property to determine how to position the child within its slot in the
VerticalStack. You can instead call the static method
Layout.LayoutChildIntoBoundingRect. This method calls
Measure on the child and uses its
VerticalOptions properties to position the child within the specified rectangle.
Often a change in an element's property affects how that element appears in layout. The layout must be invalidated to trigger a new layout.
VisualElement defines a protected method
InvalidateMeasure, which is generally called by the property-changed handler of any bindable property whose change affects the element's size. The
InvalidateMeasure method fires a
Layout class defines a similar protected method named
InvalidateLayout, which a
Layout derivative should call for any change that affects how it positions and sizes its children.
Some rules for coding layouts
Properties defined by
Layout<T>derivatives should be backed by bindable properties, and the property-changed handlers should call
Layout<T>derivative that defines attached bindable properties should override
OnAddedto add a property-changed handler to its children and
OnRemovedto remove that handler. The handler should check for changes in these attached bindable properties and respond by calling
Layout<T>derivative that implements a cache of child sizes should override
OnChildMeasureInvalidatedand clear the cache when these methods are called.
A layout with properties
WrapLayout class in the Xamarin.FormsBook.Toolkit assumes that all its children are the same size, and wraps the children from one row (or column) to the next. It defines an
Orientation property like
RowSpacing properties like
Grid, and it caches child sizes.
The PhotoWrap sample puts a
WrapLayout in a
ScrollView for displaying stock photos.
No unconstrained dimensions allowed!
UniformGridLayout in the Xamarin.FormsBook.Toolkit library is intended to display all its children within itself. Therefore, it cannot deal with unconstrained dimensions and raises an exception if one is encountered.
The PhotoGrid sample demonstrates
Layout<T> derivative can overlap its children. However, the children are rendered in their order in the
Children collection, and not the order in which their
Layout methods are called.
Layout class defines two methods that allow you to move a child within the collection:
LowerChildto move a child to the beginning of the collection
RaiseChildto move a child to the end of the collection
For overlapping children, children at the end of the collection visually appear on top of children at the beginning of the collection.
OverlapLayout class in the Xamarin.FormsBook.Toolkit library defines an attached property to indicate the render order and thus allow one of its children to be displayed on top of the others. The
StudentCardFile sample demonstrates this:
More attached bindable properties
CartesianLayout class in the
Xamarin.FormsBook.Toolkit library defines attached bindable properties to specify two
Point values and a thickness value and manipulates
BoxView elements to resemble lines.
The UnitCube sample uses that to draw a 3D cube.