Are you ready for BC27? Slight change in UserControls, big impact for me!

In the final sprint before the release of BC27, I suddenly discovered a slight change in UI behaviour that really impacted our apps. Check out the video for the whole story:

https://youtu.be/Nw9_iGKdzIs

In this video, Erik discovers a potentially breaking change in Business Central 27 that affects user controls (control add-ins) on pages that have a source table. The vertical stretch behavior no longer works as expected when a page combines a user control with data-bound elements like source tables and actions. Erik walks through diagnosing the issue and shares a JavaScript workaround to restore the expected full-height behavior.

The Problem: User Controls No Longer Stretch in BC27

Erik thought he was fully ready for BC27 — all his apps compiled, all pipelines were green. But while manually testing one of his apps on a BC27 preview sandbox, he noticed something was off. A page with a user control that previously stretched to fill the available vertical space was now showing at a fixed, small height.

To illustrate the issue, Erik created a simple app with a page that has:

  • A source table (data-bound)
  • A user control with VerticalStretch set to true
  • Actions (a menu)

On BC26, the user control’s red bar stretches from top to bottom as expected — even with a RequestedHeight of 200 specified, the vertical stretch takes precedence and fills the page.

On BC27, the same page renders the user control at just the RequestedHeight (200 pixels). Removing the requested height results in the default height of 100 pixels. The VerticalStretch property is effectively being ignored.

Narrowing Down the Cause

Erik systematically removed elements from the page to isolate the issue:

  1. Remove the menu/actions — still broken
  2. Remove the data expression — still broken
  3. Remove the source table — full bar returns!
  4. Add the source table back — small bar again

So the issue is tied to having a source table on a page that also contains a user control. This is a very common pattern — many developers build pages where the content is rendered via HTML in a user control, but they still need the page to be data-bound for actions and context.

What About the New UserControlHost Page Type?

BC27 introduces a new page type called UserControlHost. Erik tested this as well:

page 50100 "test"
{
    PageType = UserControlHost;
    layout
    {
        area(Content)
        {
            usercontrol(web; WebPageViewer)
            {
                trigger ControlAddInReady(CallbackUrl: Text)
                begin
                    this.CurrPage.web.Navigate('https://www.hougaard.com');
                end;
            }
        }
    }
}

The UserControlHost page type does render the user control at full height again. However, it comes with significant limitations:

  • It does not support a source table
  • It does not support actions

For many real-world scenarios where you need data binding and a proper Business Central menu alongside your user control, the UserControlHost page type simply isn’t sufficient.

The JavaScript Workaround

Erik dug into the browser’s DOM inspector to understand what was happening. The user control lives inside an iframe, and the chain looks like this:

  • div.control-addin-form — has the correct height
  • ↳ container div — stuck at 100px
  •   ↳ iframe
  •     ↳ document → html → body → control add-in content

The parent div.control-addin-form knows the correct height, but that height isn’t being propagated down to the iframe and its container in BC27 when a source table is present.

The fix involves using JavaScript to manually set the iframe’s height to match the parent container:

// Find the parent container that has the correct height
var heightIndicator = window.frameElement.closest('div.control-addin-form');

// Set the iframe's height and max-height to match
window.frameElement.style.height = (heightIndicator.offsetHeight - 5).toString() + 'px';
window.frameElement.style.maxHeight = (heightIndicator.offsetHeight - 5).toString() + 'px';

// Listen for resize events to keep it responsive
window.addEventListener('resize', function(event) {
    var heightIndicator = window.frameElement.closest('div.control-addin-form');
    window.frameElement.style.height = (heightIndicator.offsetHeight - 5).toString() + 'px';
    window.frameElement.style.maxHeight = (heightIndicator.offsetHeight - 5).toString() + 'px';
}, true);

Key details about this workaround:

  • window.frameElement gives you a reference to the iframe element that your control add-in lives in
  • The .closest() method traverses up the DOM tree to find the nearest ancestor matching the selector 'div.control-addin-form'
  • offsetHeight returns the actual rendered height (as opposed to height which returns a defined/styled height)
  • Both height and maxHeight need to be set — setting only height isn’t enough because a max-height CSS rule also constrains the element
  • The 5-pixel subtraction prevents a scrollbar from appearing
  • The resize event listener ensures the control adjusts when the browser window is resized
  • Note the camelCase naming convention for CSS properties in JavaScript: maxHeight instead of max-height

Why This Is Hard to Catch

Erik makes an important point about why this kind of issue slips through the cracks:

  • Pipelines won’t catch it — the code compiles perfectly fine
  • Page scripting can’t help — user controls aren’t meaningfully supported in page scripting tests
  • It’s a visual/rendering issue — you have to actually look at the page running on BC27 to notice something is wrong

Erik only discovered it because he happened to manually navigate to the affected page on a BC27 preview sandbox.

Conclusion

If you have Business Central pages that combine user controls with source tables — a very common pattern for HTML-driven content pages with standard BC menus and data binding — you need to test them on BC27. The vertical stretch behavior has changed, and your user controls may render at a tiny default height instead of filling the page. The JavaScript workaround shown here, which manually synchronizes the iframe height with its parent container, is a practical solution until Microsoft potentially addresses this. The key takeaway: always manually test your apps on new versions, because not every breaking change shows up in your build pipeline.