diff --git a/src/Controls/samples/Controls.Sample.UITests/Issues/Issue21706.xaml b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue21706.xaml
new file mode 100644
index 000000000000..33b58699304c
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue21706.xaml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample.UITests/Issues/Issue21706.xaml.cs b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue21706.xaml.cs
new file mode 100644
index 000000000000..a1afbfe41c7b
--- /dev/null
+++ b/src/Controls/samples/Controls.Sample.UITests/Issues/Issue21706.xaml.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Controls.Xaml;
+
+namespace Maui.Controls.Sample.Issues
+{
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ [Issue(IssueTracker.Github, 21706, "ImageButton stuck in PointerOver state", PlatformAffected.UWP)]
+ public partial class Issue21706 : ContentPage
+ {
+ public Issue21706()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/samples/Controls.Sample/Pages/Layouts/FlexLayoutPage.xaml b/src/Controls/samples/Controls.Sample/Pages/Layouts/FlexLayoutPage.xaml
index a4e677363a76..ccbcddf31ac0 100644
--- a/src/Controls/samples/Controls.Sample/Pages/Layouts/FlexLayoutPage.xaml
+++ b/src/Controls/samples/Controls.Sample/Pages/Layouts/FlexLayoutPage.xaml
@@ -39,6 +39,6 @@
FontSize="Large"
BackgroundColor="Pink"
HorizontalTextAlignment="Center" />
-
+
\ No newline at end of file
diff --git a/src/Controls/tests/UITests/Tests/Issues/Issue21706.cs b/src/Controls/tests/UITests/Tests/Issues/Issue21706.cs
new file mode 100644
index 000000000000..418b0b19efb8
--- /dev/null
+++ b/src/Controls/tests/UITests/Tests/Issues/Issue21706.cs
@@ -0,0 +1,25 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.AppiumTests.Issues
+{
+ public class Issue21706 : _IssuesUITest
+ {
+ public override string Issue => "ImageButton stuck in PointerOver state";
+
+ public Issue21706(TestDevice device) : base(device) { }
+
+ [Test]
+ public async Task ImageButtonStuckAfterRightClick()
+ {
+ this.IgnoreIfPlatforms(new TestDevice[] { TestDevice.Android, TestDevice.Mac, TestDevice.iOS });
+ App.WaitForElement("WaitForElement");
+ App.RightClick("WaitForElement");
+ await Task.Delay(200);
+ App.Click("OtherButton");
+ await Task.Delay(200);
+ VerifyScreenshot();
+ }
+ }
+}
diff --git a/src/Controls/tests/UITests/snapshots/windows/ImageButtonStuckAfterRightClick.png b/src/Controls/tests/UITests/snapshots/windows/ImageButtonStuckAfterRightClick.png
new file mode 100644
index 000000000000..8be6938236ec
Binary files /dev/null and b/src/Controls/tests/UITests/snapshots/windows/ImageButtonStuckAfterRightClick.png differ
diff --git a/src/Core/src/Handlers/ImageButton/ImageButtonHandler.Windows.cs b/src/Core/src/Handlers/ImageButton/ImageButtonHandler.Windows.cs
index 6fbd5501403c..9ba6791e9428 100644
--- a/src/Core/src/Handlers/ImageButton/ImageButtonHandler.Windows.cs
+++ b/src/Core/src/Handlers/ImageButton/ImageButtonHandler.Windows.cs
@@ -11,6 +11,7 @@ public partial class ImageButtonHandler : ViewHandler
Image? _image;
PointerEventHandler? _pointerPressedHandler;
+ PointerEventHandler? _pointerReleasedHandler;
protected override Button CreatePlatformView()
{
@@ -34,6 +35,7 @@ protected override Button CreatePlatformView()
protected override void ConnectHandler(Button platformView)
{
_pointerPressedHandler = new PointerEventHandler(OnPointerPressed);
+ _pointerReleasedHandler = new PointerEventHandler(OnPointerReleased);
if (_image != null)
{
@@ -43,6 +45,7 @@ protected override void ConnectHandler(Button platformView)
platformView.Click += OnClick;
platformView.AddHandler(UIElement.PointerPressedEvent, _pointerPressedHandler, true);
+ platformView.AddHandler(UIElement.PointerReleasedEvent, _pointerReleasedHandler, true);
base.ConnectHandler(platformView);
}
@@ -57,8 +60,10 @@ protected override void DisconnectHandler(Button platformView)
platformView.Click -= OnClick;
platformView.RemoveHandler(UIElement.PointerPressedEvent, _pointerPressedHandler);
+ platformView.RemoveHandler(UIElement.PointerReleasedEvent, _pointerReleasedHandler);
_pointerPressedHandler = null;
+ _pointerReleasedHandler = null;
base.DisconnectHandler(platformView);
@@ -93,7 +98,6 @@ public static void MapPadding(IImageButtonHandler handler, IImageButton imageBut
void OnClick(object sender, RoutedEventArgs e)
{
VirtualView?.Clicked();
- VirtualView?.Released();
}
void OnPointerPressed(object sender, PointerRoutedEventArgs e)
@@ -101,6 +105,11 @@ void OnPointerPressed(object sender, PointerRoutedEventArgs e)
VirtualView?.Pressed();
}
+ void OnPointerReleased(object sender, PointerRoutedEventArgs e)
+ {
+ VirtualView?.Released();
+ }
+
void OnImageOpened(object sender, RoutedEventArgs routedEventArgs)
{
VirtualView?.UpdateIsLoading(false);
diff --git a/src/TestUtils/src/UITest.Appium/Actions/AppiumTouchActions.cs b/src/TestUtils/src/UITest.Appium/Actions/AppiumTouchActions.cs
index 889b44a81da2..5b05cf10fb9a 100644
--- a/src/TestUtils/src/UITest.Appium/Actions/AppiumTouchActions.cs
+++ b/src/TestUtils/src/UITest.Appium/Actions/AppiumTouchActions.cs
@@ -68,6 +68,16 @@ CommandResponse Tap(IDictionary parameters)
{
return CommandResponse.FailedEmptyResponse;
}
+
+ if (parameters.TryGetValue("button", out var button) && button != null)
+ {
+ var buttonName = button.ToString();
+ if (!string.IsNullOrEmpty(buttonName) &&
+ buttonName.Equals("right", StringComparison.OrdinalIgnoreCase))
+ {
+ return RightClick(element.Id);
+ }
+ }
return TapElement(element);
}
else if (parameters.TryGetValue("x", out var x) &&
@@ -119,8 +129,31 @@ CommandResponse TapCoordinates(float x, float y)
return CommandResponse.SuccessEmptyResponse;
}
- CommandResponse DoubleTap(IDictionary parameters)
+
+ CommandResponse RightClick(string elementId)
{
+ // "ActionSequence" and "Actions" is not supported for right click on Windows
+ if (_appiumApp.GetTestDevice() == TestDevice.Windows)
+ {
+ _appiumApp.Driver.ExecuteScript("windows: click", new Dictionary
+ {
+ { "elementId", elementId },
+ { "button", "right" },
+ });
+ }
+ else if (_appiumApp.GetTestDevice() == TestDevice.Mac)
+ {
+ _appiumApp.Driver.ExecuteScript("macos: rightClick", new Dictionary
+ {
+ { "elementId", elementId }
+ });
+ }
+
+ return CommandResponse.SuccessEmptyResponse;
+ }
+
+ CommandResponse DoubleTap(IDictionary parameters)
+ {
var element = GetAppiumElement(parameters["element"]);
OpenQA.Selenium.Appium.Interactions.PointerInputDevice touchDevice = new OpenQA.Selenium.Appium.Interactions.PointerInputDevice(PointerKind.Touch);
diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
index e99caac17f61..8e137a774514 100644
--- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
+++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
@@ -34,6 +34,16 @@ public static void Click(this IApp app, string element)
app.FindElement(element).Click();
}
+ public static void RightClick(this IApp app, string element)
+ {
+ var uiElement = app.FindElement(element);
+ uiElement.Command.Execute("click", new Dictionary()
+ {
+ { "element", uiElement },
+ { "button", "right" }
+ });
+ }
+
public static string? GetText(this IUIElement element)
{
var response = element.Command.Execute("getText", new Dictionary()