⚠ This page is served via a proxy. Original site: https://github.com
This service does not collect credentials or authentication data.
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pop-ups of Xaml controls need positioning and dismissal

1 participant