Line anchoring explained in depth

Hello,
recently I’ve a problem with anchored lines and because of sparse documentation I needed to search forums for better explanation. I’ve found this old post Table - Combining column across rows - Anchoring not a fit where someone was solving the similar problem. It worked but I don’t know if I understand it correctly.

Let assume that I have a table with 2 rows and 2 columns. The first column should have multi-line text, that should span the 2 rows, the second column is some field (not important). So the definition looks like this:

1. row: 1. column empty; 2. column is field; no anchoring

2. row: 1. column empty; 2. column is field; no anchoring

3. row: 1. column is multi-line text; no 2. column; anchored to the 1. row, top

In this definition, if the multi-line text height is bigger than the 1. row height, then the 2. row Y position begins WHERE the multi-line text ends, therefor, there is a gap between the 1. and 2. line. So it looks like that 1. row height is expanded based on the multi-line text height and only after this is done, the 2. row is printed.

To fix this, the 2. row should be changed like this:
2. row: 1. column empty; 2. column is field; anchored to 1. row, bottom

In this case, the 2. row will ALWAYS start where the 1. row ended at the design time. So it doesn’t matter now, how large the multi-line text is, the 2. row will always start at the same position.

Is my understanding correct? If not, could someone explain it to me?

In any case I want to propose updating the documentation with more complex examples and also explaining the logic behind anchoring in more depth.

Hello,

on the DOM level every table line (header, data, group header/footer, footer) inherits an Anchor property from TableLineBase TableLineBase.Anchor.
That Anchor object (PropertyAnchor) defines:

  • Which line you are anchored to (“reference line” – in the Designer this is the line you pick in the anchoring dialog).
  • Where relative to that line you want to be drawn: via ToTop →
  • True = print above the reference line’s start
  • False = print below the reference line’s end
    PropertyAnchor, PropertyAnchor_members.

In other words:

  • The reference line is laid out first (including all wrapped/multi‑line content → final height is known).

  • The anchored line’s Y‑position is then computed from the final position of the reference, not from the original design-time position.

The improved positioning behavior is controlled by the option LlOption.ImprovedTableLineAnchoring LlOption.
With this option, non‑anchored lines are placed underneath the “tallest” line above them; anchored lines explicitly tie themselves to another line’s top or bottom.

Although the Designer uses a more visual dialog, internally it boils down to:

  • Anchor “Top” (ToTop = True):
    Start of your line = start of the reference line (same Y).
    This is typically used to “stack something above” or to share the same starting position.

  • Anchor “Bottom” (ToTop = False):
    Start of your line = end of the reference line (reference Y + reference height).
    This is the way to “follow” whatever height the reference line finally gets (e.g. due to multi‑line text).

The important consequence:
Anchoring to bottom explicitly tells LL: “Place me exactly where the reference line ends, regardless of how tall it becomes at runtime.”

As we understood - you described:

1. Row 1: Col1 empty, Col2 = field, no anchoring

2. Row 2: Col1 empty, Col2 = field, no anchoring

3. Row 3: Col1 = multi-line text (spanning visually both rows), no Col2, anchored to row 1, top

…is that correct?

With row 2 anchored to the bottom of row 1, row 2 will always start exactly where row 1 ends after considering all anchored content (like your multi-line text).
Therefore, there will be no gap; row 2 “follows” the actual height of row 1.

That’s why this setup fixes the gap in the “spanning multi-line text” scenario.

In the first setup, row 3 is anchored to row 1, but row 2 is not anchored at all.
With ImprovedTableLineAnchoring active, the engine behaves roughly like this LlOption:

1. Lay out row 1 and its anchored contents (your multi-line row 3).

2. Determine the maximum height consumed by this block.

3. Place the next non‑anchored line (row 2) below that block.

Visually, this turns your “row 3” into something that vertically extends row 1, and row 2 is moved down accordingly → the “gap” you see between what you think of as row 1 and row 2.

That’s technically correct behavior given your anchoring choice; it just doesn’t match the mental model “row 3 is a separate row that should overlap rows 1+2”.

The line-anchoring logic is affected by LlOption.ImprovedTableLineAnchoring LlOption.
There is a note in the docs that if you combine this with negative top margins in lines, you may need to disable the improved anchoring in order to get the expected result.
This can change placement characteristics subtly between LL versions.
Anchored lines / combined rows are not supported by all export formats in exactly the same visual way; some exporters do not emulate the exact internal table layout logic 1:1 (mentioned in the anchoring docs) PropertyAnchor.

Summarized:

  • A line anchored to the top of another line shares its starting Y-position with that line.
  • A line anchored to the bottom of another line starts exactly where the reference line ends after all its content (including multi-line text and anchored objects) has been laid out.
  • With ImprovedTableLineAnchoring active, non-anchored lines underneath are positioned below the full height consumed by anchored content above.

You have a very valid point about documentation clarity.
The underlying mechanisms are only described via:

None of these currently walk through a concrete “multi-line text spanning rows” example, which would make this behavior much more intuitive.
Your request for more complex examples and a conceptual explanation is something we can definitely forward internally.

Thank you for the thorough explanation. I think I get it now.