Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,37 @@ void OnPlatformViewTouched(object? sender, AView.TouchEventArgs e)
}

if (e.Event != null)
{
// Process the touch event for this view's gestures
OnTouchEvent(e.Event);

// Allow event bubbling when only drag/drop recognizers are present
// For other gestures, allow bubbling so parent behaviors (like item selection) work
if (ShouldAllowEventBubbling())
{
e.Handled = false;
}
Comment on lines +289 to +294
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

The comment on line 290 is confusing. It says 'For other gestures, allow bubbling' but the code actually prevents bubbling for other gestures (returns false in ShouldAllowEventBubbling when non-drag/drop recognizers are present). The comment should clarify that bubbling is allowed ONLY when drag/drop recognizers are present, not for other gestures.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

The logic doesn't explicitly handle the case when ShouldAllowEventBubbling() returns false. When there are tap, pan, swipe, or other gesture recognizers present, the event handling behavior is unclear since e.Handled is not explicitly set. Consider explicitly setting e.Handled = true in the else branch to make the intent clear, or document what the default behavior is when the condition is not met.

Suggested change
}
}
else
{
e.Handled = true;
}

Copilot uses AI. Check for mistakes.
}
}

bool ShouldAllowEventBubbling()
{
if (View == null)
return false;

var recognizers = View.GetCompositeGestureRecognizers();
if (recognizers == null || recognizers.Count == 0)
return false;

// Don't allow bubbling if we other recognizers than drag and drop
Copy link

Copilot AI Nov 22, 2025

Choose a reason for hiding this comment

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

Grammatical error in comment. Should be 'if we have other recognizers' instead of 'if we other recognizers'.

Suggested change
// Don't allow bubbling if we other recognizers than drag and drop
// Don't allow bubbling if we have other recognizers than drag and drop

Copilot uses AI. Check for mistakes.
foreach (var recognizer in recognizers)
{
if (recognizer is not DragGestureRecognizer && recognizer is not DropGestureRecognizer)
return false;
}

// Only drag/drop recognizers present, allow bubbling
return true;
}

void SetupElement(VisualElement? oldElement, VisualElement? newElement)
Expand Down
36 changes: 36 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue32702.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue32702"
Title="Issue 32702 - CollectionView Selection with Drag/Drop Gestures">

<VerticalStackLayout Padding="20" Spacing="10">
<Label Text="Tap items to select them"
AutomationId="InstructionLabel"/>

<Label x:Name="StatusLabel"
Text="No selection"
AutomationId="StatusLabel"/>

<CollectionView x:Name="TestCollectionView"
SelectionMode="Single"
SelectionChanged="OnSelectionChanged"
AutomationId="TestCollectionView">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding .}"
HeightRequest="50"
Padding="16,8"
BackgroundColor="LightGray"
Margin="4"
AutomationId="{Binding .}">
<Label.GestureRecognizers>
<DropGestureRecognizer AllowDrop="True" />
<DragGestureRecognizer />
</Label.GestureRecognizers>
</Label>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
38 changes: 38 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue32702.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.Collections.ObjectModel;

namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 32702, "CollectionView item selection doesn't work when DragGestureRecognizer or DropGestureRecognizer is attached to item content", PlatformAffected.Android)]
public partial class Issue32702 : ContentPage
{
public ObservableCollection<string> Items { get; set; }

public Issue32702()
{
InitializeComponent();

Items = new ObservableCollection<string>
{
"Item 1",
"Item 2",
"Item 3",
"Item 4",
"Item 5"
};

TestCollectionView.ItemsSource = Items;
}

private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.Count > 0)
{
var selectedItem = e.CurrentSelection[0].ToString();
StatusLabel.Text = $"Selected: {selectedItem}";
}
else
{
StatusLabel.Text = "No selection";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue32702 : _IssuesUITest
{
public Issue32702(TestDevice device) : base(device)
{
}

public override string Issue => "CollectionView item selection doesn't work when DragGestureRecognizer or DropGestureRecognizer is attached to item content";

[Test]
[Category(UITestCategories.CollectionView)]
public void CollectionViewSelectionWorksWithDragDropGestures()
{
// Wait for CollectionView to load
App.WaitForElement("TestCollectionView");

// Verify initial state
var statusLabel = App.WaitForElement("StatusLabel");
Assert.That(statusLabel.GetText(), Is.EqualTo("No selection"));

// Tap on first item
App.Tap("Item 1");

// Verify selection changed
Assert.That(statusLabel.GetText(), Is.EqualTo("Selected: Item 1"));

// Tap on second item
App.Tap("Item 2");

// Verify selection changed again
Assert.That(statusLabel.GetText(), Is.EqualTo("Selected: Item 2"));
}
}
Loading