Skip to content

Conversation

@Nitin-100
Copy link
Contributor

@Nitin-100 Nitin-100 commented Jan 21, 2026

Description

This PR fixes Issue #15557 - "Pop-ups of Xaml controls need positioning and dismissal"

When XAML controls with popups (like ComboBox, DatePicker, TimePicker) are hosted inside a React Native ScrollView via ContentIslandComponentView, two bugs occur:

  1. Bug 1 (Position): After scrolling, opening the popup shows it at the wrong position (offset by the scroll amount)
  2. Bug 2 (Light Dismiss): Opening a popup and then scrolling leaves the popup floating at its original position instead of dismissing

Root Cause

  • Bug 1: ContentIslandComponentView uses ChildSiteLink.LocalToParentTransformMatrix for popup positioning, but wasn't being notified when scroll position changed
  • Bug 2: XAML light dismiss behavior wasn't being triggered when scroll begins in React Native

Solution

Bug 1 Fix: Popup Position After Scroll

  • ScrollViewComponentView now fires LayoutMetricsChanged event when scroll position changes
  • ContentIslandComponentView listens to this and calls ParentLayoutChanged()
  • This updates LocalToParentTransformMatrix with the correct scroll offset via getClientRect()

Bug 2 Fix: Light Dismiss on Scroll (Generic for ALL 3rd Party XAML Controls)

  • Added SetXamlRoot(XamlRoot) method to ContentIslandComponentView API
  • 3rd party XAML components register their XamlRoot after the element is loaded
  • When scroll begins, DismissPopups() uses VisualTreeHelper.GetOpenPopupsForXamlRoot() to find and close ALL open XAML popups
  • This is a generic solution that works for ANY XAML control (ComboBox, DatePicker, TimePicker, Flyouts, etc.)

Files Changed

Core Fix (vnext/Microsoft.ReactNative/)

  • CompositionComponentView.idl - Added SetXamlRoot() method to ContentIslandComponentView
  • Fabric/Composition/ContentIslandComponentView.h/.cpp - Implemented SetXamlRoot() and DismissPopups()
  • Fabric/Composition/ScrollViewComponentView.h/.cpp - Added scroll position change notification and popup dismissal trigger

Sample Component (packages/sample-custom-component/)

  • Added ComboBox component as example of proper 3rd party implementation
  • Shows how to use SetXamlRoot() for popup dismissal

Test Samples

  • XamlPopupRepro.windows.tsx - Test case in RNTester
  • xamlPopupBug.tsx - Test case in Playground

🔧 Guidance for 3rd Party XAML Component Developers

If you're building a custom XAML component that has popups (ComboBox, DatePicker, Flyouts, etc.), follow this pattern to ensure proper popup behavior inside React Native ScrollViews:

C++ Implementation Pattern

void YourComponentView::InitializeContentIsland(
    const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView) noexcept {
  
  // 1. Create your XamlIsland and XAML control
  m_xamlIsland = winrt::Microsoft::UI::Xaml::XamlIsland{};
  m_yourControl = winrt::Microsoft::UI::Xaml::Controls::YourControl{};
  
  // 2. Set up the control
  // ... configure your control ...
  
  // 3. Set content and connect
  m_xamlIsland.Content(m_yourControl);
  islandView.Connect(m_xamlIsland.ContentIsland());
  
  // 4. IMPORTANT: Register XamlRoot for popup dismissal (Issue #15557)
  // This enables automatic popup closing when parent ScrollView scrolls
  m_yourControl.Loaded([islandView, this](auto const &, auto const &) {
    if (auto xamlRoot = m_yourControl.XamlRoot()) {
      islandView.SetXamlRoot(xamlRoot);
    }
  });
}

Why This Works

  1. Position Fix: The framework automatically updates LocalToParentTransformMatrix when scroll position changes, so your popup appears at the correct location.

  2. Light Dismiss: When you register your XamlRoot, the framework uses VisualTreeHelper.GetOpenPopupsForXamlRoot() to find ALL open popups when scroll begins and closes them automatically.

What else handled with this fix

  • Correct popup positioning after scroll
  • Automatic popup dismissal when scroll starts (light dismiss)
  • Works with ANY XAML popup type (ComboBox dropdown, DatePicker calendar, Flyouts, etc.)
  • No component-specific code needed in the framework

Testing

  1. Run the XamlPopupRepro sample in Playground
  2. Scroll down in the ScrollView
  3. Click a ComboBox to open dropdown - should appear at correct position
  4. Open a ComboBox dropdown, then scroll - dropdown should close automatically
Microsoft Reviewers: Open in CodeFlow

…#15557)

- Add SetXamlRoot() API on ContentIslandComponentView for 3rd party XAML components
- Add DismissPopups() using VisualTreeHelper.GetOpenPopupsForXamlRoot()
- Fire LayoutMetricsChanged on scroll to update popup positions
- Dismiss child ContentIsland popups when scroll begins
- Add ComboBox sample component demonstrating the pattern
- Add xamlPopupBug test sample for playground-composition
@Nitin-100 Nitin-100 requested review from a team as code owners January 21, 2026 07:49
Copy link
Contributor

@sundaramramaswamy sundaramramaswamy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address comments.

Comment on lines +88 to +93
// Issue #15557: Register the XamlRoot for this ContentIsland to enable popup dismissal.
// When a parent ScrollView starts scrolling, DismissPopups() will be called which uses
// VisualTreeHelper.GetOpenPopupsForXamlRoot() to find and close all open XAML popups.
// 3rd party XAML components (ComboBox, DatePicker, etc.) should call this after creating
// their XamlIsland: islandView.SetXamlRoot(m_xamlIsland.Content().XamlRoot());
void SetXamlRoot(Microsoft.UI.Xaml.XamlRoot xamlRoot);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ContentIsland is generic. Xaml 3P modules just ride on it but any other island (inherited from Microsoft.Ui.ContentIsland) can be hosted. The core "knowing" about Xaml is a problem. Instead of the core dismissing the popup (and there by knowing about Xaml) it should just tell the module to dismiss pop-up if any.

Try to see if you could emit an existing event to which the 3P component can listen and dismiss pop-ups.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please drop the re-formatting and only keep your change. Two reasons

  1. What changed isn't evident
  2. It screws up git blame output

@microsoft-github-policy-service microsoft-github-policy-service bot added the Needs: Author Feedback The issue/PR needs activity from its author (label drives bot activity) label Jan 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs: Author Feedback The issue/PR needs activity from its author (label drives bot activity)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pop-ups of Xaml controls need positioning and dismissal

2 participants