KEMBAR78
[Windows]Fixed the PointerGestureRecognizer behaves incorrectly when multiple windows are open. by Ahamed-Ali · Pull Request #30537 · dotnet/maui · GitHub
Skip to content

Conversation

Ahamed-Ali
Copy link
Contributor

@Ahamed-Ali Ahamed-Ali commented Jul 10, 2025

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Root Cause of the issue

  • Events from the other (minimized) window are interfering with the active (live) window.

Description of Change

  • Only allow pointer events to be handled if they originate from the currently active (live) window

This ensures:

  • Minimized or background windows do not interfere with active window interactions.

  • Pointer events are scoped and filtered based on the correct visual root or window handle.

Issues Fixed

Fixes #30536
Fixes #27430

Tested the behaviour in the following platforms

  • Android
  • Windows
  • iOS
  • Mac

Screenshot

Before Issue Fix After Issue Fix
WithFlickerofPointerGestures.mp4
WithFixOfPointerGestures.mp4

@dotnet-policy-service dotnet-policy-service bot added the community ✨ Community Contribution label Jul 10, 2025
@dotnet-policy-service
Copy link
Contributor

Hey there @@Ahamed-Ali! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@dotnet-policy-service dotnet-policy-service bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Jul 10, 2025
@Ahamed-Ali Ahamed-Ali marked this pull request as ready for review July 10, 2025 13:48
@Copilot Copilot AI review requested due to automatic review settings July 10, 2025 13:48
@Ahamed-Ali Ahamed-Ali requested a review from a team as a code owner July 10, 2025 13:48
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR ensures that pointer events are only processed for the currently active window on Windows, preventing background or minimized windows from interfering with live interactions.

  • Adds a relevance check (IsPointerEventRelevantToCurrentElement) before dispatching pointer gestures.
  • Early-returns from HandlePgrPointerEvent when events originate from a different or inactive window.
  • Encapsulates window-root comparison logic in a private helper with exception safety.

Comment on lines 702 to 699
catch
{
Copy link

Copilot AI Jul 10, 2025

Choose a reason for hiding this comment

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

[nitpick] Catching all exceptions silently may mask underlying issues. Consider catching specific exception types and at least logging unexpected errors for diagnostics.

Suggested change
catch
{
catch (Exception ex)
{
// Log the exception for diagnostics
_logger?.LogError(ex, "An error occurred while validating pointer event relevance.");

Copilot uses AI. Check for mistakes.

Copy link
Member

Choose a reason for hiding this comment

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

@Ahamed-Ali why are we catching all exceptions here instead of logging or throwing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have now logged the exception. @PureWeen

@PureWeen PureWeen added this to the .NET 9 SR10 milestone Jul 10, 2025
@PureWeen PureWeen added the p/0 Work that we can't release without label Jul 10, 2025
@PureWeen PureWeen moved this from Todo to Ready To Review in MAUI SDK Ongoing Jul 11, 2025

bool IsPointerEventRelevantToCurrentElement(PointerRoutedEventArgs e)
{
if (_container is null)
Copy link
Contributor

Choose a reason for hiding this comment

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

Here, can do an early validation of critical dependencies, can include line 690.

if (_container?.XamlRoot is null || e?.OriginalSource is null)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have optimized the fix as suggested @jsuarezruiz

@PureWeen
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@PureWeen
Copy link
Member

/backport to release/9.0.1xx-sr9

@github-actions
Copy link
Contributor

Started backporting to release/9.0.1xx-sr9: https://github.com/dotnet/maui/actions/runs/16334943485

Copy link
Contributor

@jsuarezruiz jsuarezruiz left a comment

Choose a reason for hiding this comment

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

@github-project-automation github-project-automation bot moved this from Ready To Review to Changes Requested in MAUI SDK Ongoing Jul 18, 2025
@Ahamed-Ali
Copy link
Contributor Author

Could we include a device test here https://raw.githubusercontent.com/dotnet/maui/refs/heads/main/src/Controls/tests/DeviceTests/Elements/Window/WindowTests.Windows.cs?

I tried adding a device test, but couldn't reproduce the issue without the fix. When using the mouse pointer manually, the issue can be reproduced. However, it cannot be reproduced in the test using the SendPointerEntered method via reflection. As a result, the test passes both with and without the fix. @jsuarezruiz

Below is the code used in the device test to simulate the issue scenario.

    [Fact(DisplayName = "PointerExited should not fire when PointerEntered is triggered after minimizing a second window")]
    public async Task PointerExited_ShouldNotFire_WhenPointerEnteredTriggeredInMainWindow_AfterMinimizingSecondWindow()
    {
      SetupBuilder();
 
      // Create a ContentPage with an AbsoluteLayout that uses PointerGestureRecognizer
      var absoluteLayout = new AbsoluteLayout
      {
        WidthRequest = 256,
        HeightRequest = 256,
        BackgroundColor = Colors.Red,
        IsClippedToBounds = true
      };
 
      var pointerEnteredFired = false;
      var pointerExitedFired = false;
 
      var pointerRecognizer = new PointerGestureRecognizer();
      pointerRecognizer.PointerEntered += (s, e) =>
      {
        absoluteLayout.BackgroundColor = Colors.Lime;
        pointerEnteredFired = true;
      };
 
      pointerRecognizer.PointerExited += (s, e) =>
      {
        absoluteLayout.BackgroundColor = Colors.Red;
        pointerExitedFired = true;
      };
 
      absoluteLayout.GestureRecognizers.Add(pointerRecognizer);
 
      var mainPage = new ContentPage { Content = absoluteLayout };
      Window mainWindow = null;
 
      await CreateHandlerAndAddToWindow<IWindowHandler>(mainPage, async (handler) =>
      {
        mainWindow = handler.VirtualView as Window;
        Assert.NotNull(mainWindow);
 
        // Create a new window
        var secondWindow = new Window (new ContentPage
        {
          Content = new Label { Text = "Second Window", HorizontalOptions = LayoutOptions.Center, VerticalOptions = LayoutOptions.Center }
        });
        Application.Current.OpenWindow(secondWindow);

        await Task.Delay(500); // Wait for window to open
 
        // Get the platform window for the second window
        var secondPlatformWindow = secondWindow.Handler?.PlatformView as Microsoft.UI.Xaml.Window;
        Assert.NotNull(secondPlatformWindow);
 
        // Minimize the second window
        secondPlatformWindow.Minimize();
        await Task.Delay(500); // Wait for minimize animation
 
        // Simulate pointer entered event using reflection to call the internal SendPointerEntered method
        await InvokeOnMainThreadAsync(() =>
        {
          // Use reflection to call the internal SendPointerEntered method on PointerGestureRecognizer
          var sendPointerEnteredMethod = typeof(PointerGestureRecognizer).GetMethod("SendPointerEntered",
            BindingFlags.NonPublic | BindingFlags.Instance);
 
          sendPointerEnteredMethod?.Invoke(pointerRecognizer, new object[] { absoluteLayout, null, null });
        });
 
        await Task.Delay(500);
 
        // Verify the PointerEntered event was triggered and changed the background color
        Assert.Equal(Colors.Lime, absoluteLayout.BackgroundColor);
        Assert.True(pointerEnteredFired, "PointerEntered event did not fire");
        Assert.True(!pointerExitedFired, "PointerExited event Should not fire");
        // Close the second window
        Application.Current.CloseWindow(secondWindow);
      });
    }

@PureWeen
Copy link
Member

/azp run

@azure-pipelines
Copy link

Azure Pipelines successfully started running 3 pipeline(s).

@PureWeen
Copy link
Member

PureWeen commented Aug 5, 2025

/rebase

@Ahamed-Ali
Copy link
Contributor Author

Thank you for more details, @Ahamed-Ali! After further investigation, it seems to actually be related to enabling the ExtendsContentIntoTitleBar on the second Window, the Pointer Entered/Exited events gets called repeatedly. which is an already known WinUI issue: microsoft/microsoft-ui-xaml#10572 (there are already a few similar linked to the issue).

Thanks for the investigation. The issue is present in the WinUI sample when using the latest Microsoft.WindowsAppSDK package version 1.7.250606001.

@github-project-automation github-project-automation bot moved this from Changes Requested to Approved in MAUI SDK Ongoing Aug 11, 2025
@mattleibow
Copy link
Member

Is it possible to add a UI test? This looks like it is a "simeple thing to repo" and is "consistent" so a UI test should be ok if we wiggle the mouse or something?

I saw this one microsoft/microsoft-ui-xaml#10357 seem to trigger the weird on a click.

@PureWeen PureWeen merged commit 3f0a7c6 into dotnet:main Aug 11, 2025
129 checks passed
@github-project-automation github-project-automation bot moved this from Approved to Done in MAUI SDK Ongoing Aug 11, 2025
@PureWeen
Copy link
Member

/backport to release/9.0.1xx-sr9

@github-actions
Copy link
Contributor

Started backporting to release/9.0.1xx-sr9: https://github.com/dotnet/maui/actions/runs/16890516893

SuthiYuvaraj pushed a commit to SuthiYuvaraj/maui that referenced this pull request Aug 12, 2025
…multiple windows are open. (dotnet#30537)

* Fixed the PointerGestureRecognizer events from another window interfere with the active window

* Optimize the fix

* Logging the error

* missed paramter

* - add some code to debounce when there are multi window scenarios

---------

Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
rmarinho pushed a commit that referenced this pull request Aug 13, 2025
…multiple windows are open. (#30537)

* Fixed the PointerGestureRecognizer events from another window interfere with the active window

* Optimize the fix

* Logging the error

* missed paramter

* - add some code to debounce when there are multi window scenarios

---------

Co-authored-by: Shane Neuville <shneuvil@microsoft.com>
@github-actions github-actions bot locked and limited conversation to collaborators Sep 11, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-gestures Gesture types community ✨ Community Contribution p/0 Work that we can't release without partner/syncfusion Issues / PR's with Syncfusion collaboration

Projects

Status: Done

5 participants