Skip to content

Add MapLongClicked event to Map control#33792

Merged
jfversluis merged 3 commits intonet11.0from
feature/map-long-clicked
Mar 2, 2026
Merged

Add MapLongClicked event to Map control#33792
jfversluis merged 3 commits intonet11.0from
feature/map-long-clicked

Conversation

@jfversluis
Copy link
Copy Markdown
Member

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!

Description of Change

Adds a MapLongClicked event to the Map control that fires when the user performs a long press/hold gesture on the map. This is a common pattern for "add pin at location" scenarios.

Platforms Implemented

  • Android: Uses native MapLongClick event from Google Maps SDK
  • iOS/MacCatalyst: Uses UILongPressGestureRecognizer with proper state filtering

Implementation Details

  • Only fires once per long press (iOS checks State == Began)
  • Does not fire when long pressing on pins/annotations (filtered via ShouldReceiveTouch)
  • Coexists with existing MapClicked event without interference
  • Reuses existing MapClickedEventArgs class

Public API Changes

public class Map
{
    /// <summary>
    /// Occurs when the user long-presses/holds on the map control.
    /// </summary>
    public event EventHandler<MapClickedEventArgs>? MapLongClicked;
}

public interface IMap
{
    /// <summary>
    /// Method called by the handler when user long-presses on the Map.
    /// </summary>
    void LongClicked(Location position);
}

Usage Example

map.MapLongClicked += (sender, e) =>
{
    // Add a pin at the long-pressed location
    map.Pins.Add(new Pin
    {
        Label = "New Pin",
        Location = e.Location
    });
};

Testing

  • ✅ 7 unit tests added covering:
    • Event fires correctly with location data
    • Sender is the Map instance
    • Coexistence with MapClicked (no interference)
    • Multiple handlers support
    • Handler removal
    • No exception when no handler attached
  • ✅ Sample gallery page added (MapLongClickGallery)
  • ✅ Builds verified for Android and iOS

Related

Part of Maps Epic: #33787

Copilot AI review requested due to automatic review settings January 30, 2026 13:29
@jfversluis jfversluis added this to the .NET 11.0-preview1 milestone Jan 30, 2026
Copy link
Copy Markdown
Contributor

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

Adds a MapLongClicked event to the Map control and the corresponding IMap.LongClicked(Location) handler hook, enabling long-press location scenarios (e.g., “drop a pin here”) with Android and iOS/MacCatalyst implementations.

Changes:

  • Introduces Map.MapLongClicked and IMap.LongClicked(Location) public API surface.
  • Wires platform implementations: Google Maps MapLongClick (Android) and UILongPressGestureRecognizer (iOS/MacCatalyst).
  • Adds unit tests and a new sample gallery page demonstrating the feature.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Core/maps/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt Adds new IMap.LongClicked(Location) API entry (netstandard).
src/Core/maps/src/PublicAPI/net/PublicAPI.Unshipped.txt Adds new IMap.LongClicked(Location) API entry (net).
src/Core/maps/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt Adds new IMap.LongClicked(Location) API entry (net-windows).
src/Core/maps/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt Adds new IMap.LongClicked(Location) API entry (net-tizen).
src/Core/maps/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Adds new IMap.LongClicked(Location) API entry (net-maccatalyst).
src/Core/maps/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt Adds new IMap.LongClicked(Location) API entry (net-ios).
src/Core/maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txt Adds new IMap.LongClicked(Location) API entry (net-android).
src/Core/maps/src/Platform/iOS/MauiMKMapView.cs Adds iOS/MacCatalyst long-press gesture recognizer and routes to VirtualView.LongClicked(...).
src/Core/maps/src/Handlers/Map/MapHandler.Android.cs Subscribes/unsubscribes to GoogleMap MapLongClick and routes to VirtualView.LongClicked(...).
src/Core/maps/src/Core/IMap.cs Adds LongClicked(Location) to the handler-facing map interface.
src/Controls/Maps/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt Adds new Map.MapLongClicked event API entry (netstandard).
src/Controls/Maps/src/PublicAPI/net/PublicAPI.Unshipped.txt Adds new Map.MapLongClicked event API entry (net).
src/Controls/Maps/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt Adds new Map.MapLongClicked event API entry (net-windows).
src/Controls/Maps/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt Adds new Map.MapLongClicked event API entry (net-tizen).
src/Controls/Maps/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Adds new Map.MapLongClicked event API entry (net-maccatalyst).
src/Controls/Maps/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt Adds new Map.MapLongClicked event API entry (net-ios).
src/Controls/Maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txt Adds new Map.MapLongClicked event API entry (net-android).
src/Controls/Maps/src/Map.cs Declares the new MapLongClicked event on the control.
src/Controls/Maps/src/HandlerImpl/Map.Impl.cs Implements IMap.LongClicked(...) to raise MapLongClicked.
src/Controls/tests/Core.UnitTests/MapTests.cs Adds unit tests validating event raising semantics and handler add/remove behavior.
src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapsGallery.cs Adds navigation entry for the new sample gallery.
src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapLongClickGallery.xaml.cs New sample page code-behind demonstrating adding pins on long press.
src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapLongClickGallery.xaml New sample UI wiring MapLongClicked to the handler.

@rmarinho
Copy link
Copy Markdown
Member

/rebase

@jfversluis
Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@jfversluis
Copy link
Copy Markdown
Member Author

/azp run maui-pr

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Feb 26, 2026

Independent Code Review

Summary: Adds MapLongClicked event to the Map control, with Android (GoogleMap.MapLongClick) and iOS (UILongPressGestureRecognizer) implementations.

✅ What Looks Good

  • Clean, minimal implementation following the existing MapClicked pattern exactly
  • iOS correctly checks recognizer.State != UIGestureRecognizerState.Began to avoid duplicate callbacks
  • Proper event subscribe/unsubscribe lifecycle on Android
  • iOS cleanup properly removes and disposes the gesture recognizer
  • Good test coverage (7 tests including coexistence, handler removal, multiple handlers)

🔴 Issues

1. Reuses MapClickedEventArgs instead of a dedicated type
MapLongClicked fires EventHandler<MapClickedEventArgs>. While pragmatic, this means there's no way to distinguish a click from a long-click via the event args type alone. A MapLongClickedEventArgs (even if identical) would be more semantically correct and forward-compatible if long-click-specific data is ever needed.

2. No Windows implementation
The PR adds Android and iOS support but Windows has no MapLongClick handler. Unlike some map features, Windows Bing Maps does support long-press detection. Missing this will silently not work.

🟡 Nits

  • Unit test variables like Location receivedLocation = null should use Location? receivedLocation = null for nullable correctness.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 26, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 33792

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 33792"

@jfversluis
Copy link
Copy Markdown
Member Author

Thanks for the review @kubaflo! Responses:

1. Reuses MapClickedEventArgs instead of a dedicated type
This is intentional. Both events provide the same data (a \Location). Creating a separate \MapLongClickedEventArgs\ with identical properties would add API surface without value. If long-click-specific data is ever needed, a new event args type can be introduced at that point without breaking changes (the event type can be widened from \EventHandler\ to \EventHandler\ where \MapLongClickedEventArgs : MapClickedEventArgs).

2. No Windows implementation
The Windows Maps control uses Bing Maps UWP which has limited gesture API. \MapControl\ doesn't expose a native long-press/right-click event in the same way. This can be considered for a future enhancement — possibly using \RightTapped\ event as a proxy on desktop. For now, the Android + iOS coverage matches the primary mobile use case.

3. Nullable correctness in tests
Fixed in the latest commit — used null-forgiving operator (
ull!) since the test file doesn't have #nullable enable.

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Feb 27, 2026

🔍 Round 2 Review — PR #33792 (Map.MapLongClicked)

Updated with author response analysis

✅ Fixed Since Round 1

  • Test variables now use null! instead of null, fixing nullable warnings.
  • Test coverage is comprehensive — coexistence test, handler removal, multiple handlers, and no-handler-attached scenarios are all covered.
  • iOS implementation correctly uses UIGestureRecognizerState.Began to avoid multiple callbacks during the long press lifecycle.
  • Android cleanup properly unsubscribes Map.MapLongClick in DisconnectHandler.

⚠️ Issues & Author Responses

1. Reusing MapClickedEventArgs instead of dedicated typeRetracted
Author makes a valid point: both events provide identical data (a Location). Creating MapLongClickedEventArgs with the same properties adds API surface without value. More importantly, the type can later be widened via MapLongClickedEventArgs : MapClickedEventArgs without breaking changes. This is a sound API design decision. ✅

2. No Windows implementationAcknowledged as platform limitation
Author explains Windows MapControl (Bing Maps UWP) doesn't expose a native long-press event. RightTapped could be used as a desktop proxy in a future enhancement. The Android + iOS coverage matches the primary mobile use case. ✅

3. Breaking IMap interface changeAccepted per author's design intent
Author (across multiple PRs) explains that IMap is an internal contract between MAUI controls and handlers, not intended for external implementation. While technically public, this is consistent with how the MAUI handler pattern works — IView, IButton, etc. are all handler contracts. Accepting this rationale for all 8 PRs. ✅

👍 What Looks Good

  • Clean event wiring pattern matching existing MapClicked convention
  • Proper coordinate conversion on both Android and iOS
  • Sample page demonstrates the feature clearly

Summary

All original concerns have been satisfactorily addressed. This PR is clean and ready.

@jfversluis
Copy link
Copy Markdown
Member Author

/azp run maui-pr

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

kubaflo
kubaflo previously approved these changes Feb 27, 2026
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Feb 27, 2026

📋 Round 3 — PR #33792 (Map.MapLongClicked) — Final Recommendation

No outstanding issues. All Round 2 concerns were satisfactorily addressed.

Recommendation: Merge when CI passes. Clean and ready. The Build Analysis failure is infrastructure-level (identical across all 8 map PRs).

jfversluis and others added 3 commits March 1, 2026 18:17
- Add LongClicked(Location) method to IMap interface
- Add MapLongClicked event to Map class
- Implement IMap.LongClicked in Map.Impl.cs
- Add Android support via MapLongClick event handler
- Add iOS support via UILongPressGestureRecognizer
- Add unit tests for MapLongClicked event
- Add MapLongClickGallery sample page
- Update PublicAPI.Unshipped.txt files
- Add test for MapClicked and MapLongClicked coexistence
- Add test for no-handler case (doesn't throw)
- Add test for multiple handlers all firing
- Add test for handler removal
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jfversluis jfversluis force-pushed the feature/map-long-clicked branch from 0d7ba31 to d1dfc1b Compare March 1, 2026 17:26
@jfversluis jfversluis disabled auto-merge March 2, 2026 10:44
@jfversluis jfversluis merged commit c0073ff into net11.0 Mar 2, 2026
25 of 29 checks passed
@jfversluis jfversluis deleted the feature/map-long-clicked branch March 2, 2026 10:44
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

🚨 API change(s) detected @davidortinau FYI

@dotnet dotnet deleted a comment from dotnet-policy-service bot Mar 2, 2026
@github-actions github-actions bot locked and limited conversation to collaborators Apr 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants