Nevron Open Vision Documentation
Measure and Arrange

 
Measure and Arrange Overview

The layout phase of DOM documents consists of two distinct passes - Measure and Arrange, that are iteratively performed during the document evaluation. During the layout phase the document validates the measure and arrange of elements, whose measure or arrange has been invalidated in-between document evaluations. The measure and arrange passes are iteratively performed until there are elements whose measure or arrange is marked as invalid, or the measure-arrange cycle has been performed as many times as the value of the NDocument-MaxMeasureArrangeCycles property.

Elements that want to participate in the document measure and/or arrange need to implement the INMeasureElement and/or INArrangeElement interfaces. Such elements are called targets for measure and arrange. Elements that want to be notified about changes that occur in their children measure and/or arrange targets need to implement the INMeasureElementParent and/or INArrangeElementParent interfaces respectively. Such elements are called parents for measure and arrange. Conceptually this means that there are two types of elements that participate in the measure and arrange:

  1. Measure and Arrange Targets (or simply Targets)
  2. Measure and Arrange Parents (or simply Parents)
Typically an element will implement the INMeasureElement and INArrangeElement in a pair. The same is also valid for the parent interfaces. The rest of the topic talks about measure and arrange target and parent elements, assuming that these interfaces are implemented in pairs. This is however not mandatory - meaning that the measure tree can be completely different from the arrange tree.

A single element can be a Target, Parent or both a Target and Parent for measure and arrange. If you look only at the elements in a DOM hierarchy that are either Targets or Parents for either measure or arrange, you will see a forest of trees, as shown in the following diagram:

 

figure 1. Measure and Arrange Trees

From the image we can clearly see that elements that are not Targets or Parents (element D) are excluded from the measure and arrange trees (i.e. the measure and arrange parent of H and I is B).

Elements that are considered as Targets will be deferly measured by the document in leafs-to-root order. For example: if C, G and F have invalid measure, the document will validate their measure in the following order: G, F, C. Reversely, if the same elements have invalid arrange, the document will validate their arrange in root-to-leafs order: C, G, F.

The general idea of the layout phase is to provide the basics for deferred incremental layout. In such a layout, when elements arrange their children they typically need to know their desired size. Because the desired size of an element is typically a function of its content and children, the measure pass is performed in leafs-to-root order (e.g. measure the children and then measure the parent). Reversely, the final arrangement of the elements is performed in root-to-leafs order (e.g. arrange the parent and then arrange the children).

The concept of a desired size is introduced by higher level elements (see Box Elements for example), because the document does not generally care how the layout is actually performed - it just provides the means for deferred callbacks in a specific order. The measure and arrange passes are iteratively performed until there are elements with invalid measure or arrange, in order to provide resolution for complex layout cases, where certain elements measure may be affected by their arrangement (a typical example is the NWrapFlowPanel - it needs to know its arrange size in order to provide a desired size). A cornerstone concept in such cases is to implement a certain layout stiffening that would incrementally stiffen (fix) the desired size as the measure and arrange passes progress.

Elements that are marked as only Parent elements are the roots of the measure and arrange tree. Such elements are typically used when you want to break the measure and arrange tree at some level. If an element is both a Target and Parent for measure and arrange, it is up to it to decide whether notifications send by its child Target elements affect its measure or arrange or not.

Parent interfaces allow the placement of an arbitrary Target element in an arbitrary hierarchy. In this way the Target element does not actually care about the parent in which it resides. For example: a NOV widget to be placed in another widget or a NOV diagram shape. If the widget was designed to be placed only in another widget, the integration of the widget inside a shape would not have been so easy. Since the widget is only interested in the respective parent interface, it is possible to integrate a widget in any type of NOV content.

 

Implementing INMeasureElement and INArrangeElement

The INMeasureElement and INArrangeElement interfaces are typically implemented by the fundamental elements of each specific application area (for example these interfaces are implemented by Box Elements that sit pretty low in the NOV UI elements hierarchy), so most likely you will not need to implement these interfaces directly. Some knowledge about the pattern in which these interfaces are implemented is however useful, if you want to understand the measure and arrange system more deeply.

The INMeasureElement and INArrangeElement interfaces expose the basic states in which a measure and arrange target can generally be:

The following sample represents a template INMeasureElement and INArrangeElement implementation:

Implementing INMeasureElement and INArrangeElement
Copy Code
public class MyMeasureAndArrangeTarget : NElement, INMeasureElement, INArrangeElement
{
    public MyMeasureAndArrangeTarget() { }
    static MyMeasureAndArrangeTarget()
    {
        MyMeasureAndArrangeTargetSchema = NSchema.Create(typeof(MyMeasureAndArrangeTarget), NElement.NElementSchema);
    }      
    // INMeasureElement Implementation
    public bool IsMeasureValid
    {
        get { return NMeasureElementHelpers.GetIsMeasureValid(this); }
    }
    public bool IsPooledForMeasure
    {
        get { return NMeasureElementHelpers.GetIsPooledForMeasure(this); }
    }
    public void InvalidateMeasure(object hint)
    {
        if (NMeasureElementHelpers.GetIsMeasureValid(this))
        {
            // TODO: invalidate any measure data here
            NMeasureElementHelpers.SetIsMeasureValid(this, false);
        }
        if (!NMeasureElementHelpers.GetIsPooledForMeasure(this) && OwnerDocument != null)
        {
            OwnerDocument.PoolForMeasureValidation(this);
        }
    }
    public void ValidateMeasure(NLayoutVisitor visitor)
    {
        // TODO: perform the element measure here
        NMeasureElementHelpers.SetIsMeasureValid(this, true);
    }
    // INArrangeElement
    public bool IsArrangeValid
    {
        get { return NArrangeElementHelpers.GetIsArrangeValid(this); }
    }
    public bool IsPooledForArrange
    {
        get { return NArrangeElementHelpers.GetIsPooledForArrange(this); }
    }
    public void InvalidateArrange(object hint)
    {
        if (NArrangeElementHelpers.GetIsArrangeValid(this))
        {
            // TODO: invalidate any arrange data here
            NArrangeElementHelpers.SetIsArrangeValid(this, false);
        }
        if (!NArrangeElementHelpers.GetIsPooledForArrange(this) && OwnerDocument != null)
        {
            OwnerDocument.PoolForArrangeValidation(this);
        }
    }
    public void ValidateArrange(NLayoutVisitor visitor)
    {
        // TODO: perform the element arrange here
        NArrangeElementHelpers.SetIsArrangeValid(this, true);
    }
    // schema
    public static readonly NSchema MyMeasureAndArrangeTargetSchema;
}

It is a developer responsibility to call the InvalidateMeasure or InvalidateArrange, whenever the element needs to be remeasured or rearranged at the next document evaluation. As mentioned before, the purpose of invalidating the measure or arrange is to mark the element measure or arrange as invalid and to pool it for measure or arrange validation.

Both InvalidateMeasure and InvalidateArrange are accepting an object argument called hint. The purpose of the hint object is to hint the implementation about a specific aspect of the measure or arrange that needs to be invalidated. Measure and arrange targets are free to implement invalidation hinting as needed, because the document does not play any role in the accumulation of invalidation hints.

The measure or arrange validation (i.e. the document calling the ValidateMeasure or ValidateArrange methods of the element) is performed deferly at the next document evaluation.

Typically you need to invalidate the element measure or arrange when some properties or children of the element change. The next section discusses the meta markup available on schemas and properties that can help you automatically invalidate the measure and arrange in the most common conditions.

Measure and Arrange Meta Markup

In order to facilitate the invalidation of the element measure or arrange, each NProperty provides several flags that you can raise in order to automatically invalidate the measure or arrange of the element, to which the property belongs, when the computed value of that property changes. These flags are set on a per schema basis, via the NProperty-SetAffectsMeasure and NProperty-SetAffectsArrange methods (the property metadata is discussed in the Properties topic). For example:

Affects Measure and Arrange Property Markup
Copy Code
public class MyMeasureAndArrangeTarget : NElement, INMeasureElement, INArrangeElement
{
...
    static MyMeasureAndArrangeTarget()
    {
        MyMeasureAndArrangeTargetSchema = NSchema.Create(typeof(MyMeasureAndArrangeTarget), NElement.NElementSchema);
       
        // mark the SomeBool property as affecting the measure and arrange of the element
        SomeBoolProperty = MyMeasureAndArrangeTargetSchema.AddSlot("SomeBool", NDomType.Boolean, false);
        SomeBoolProperty.SetAffectsMeasure(true);
        SomeBoolProperty.SetAffectsArrange(true);
    }
...
}
The element must implement the INMeasureElement interface, if a property that belongs to it needs to be marked as affecting the measure. Respectively the element must implement the INArrangeElement interface, if a property that belongs to it needs to be marked as affecting the element arrange, as shown in the example.

When the computed value of the SomeBool property changes, the element will call the InvalidateMeasure and InvalidateArrange methods for you, because the property is marked as affecting both the measure and the arrange of the element.

Calls to InvalidateMeasure and InvalidateArrange caused by meta-markup are associated with a null hint.

When a measure and arrange target enters the document, it automatically informs its measure and arrange parent. In the most common case when an element measure and arrange target is also a measure and arrange parent, the element simply invalidates its measure and arrange. This means that in most cases the invalidation of measure and arrange caused by elements being added or removed is automatically handled by the DOM. There are cases however when for certain elements you need to manually specify that its children affect the element measure or arrange or parent measure or arrange. Consider a measure and arrange target that is a collection of simple elements (not measure and arrange targets), and the target measure and arrange needs to be invalidated when child elements are added or removed from it. In this case you can use the NSchema-SetChildrenAffectMeasure and NSchema-SetChildrenAffectArrange methods, to inform the DOM that whenever a child element is added or removed to the schema instance elements, the DOM needs to invalidate its measure and arrange respectively. 

See Also
Send Feedback