Add Pin.ShowInfoWindow() and Pin.HideInfoWindow() methods#33985
Add Pin.ShowInfoWindow() and Pin.HideInfoWindow() methods#33985jfversluis merged 5 commits intonet11.0from
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new cross-platform API surface to programmatically show/hide a map pin’s info window (callout), wiring the request through IMap/handler commands and exposing convenience methods on Pin.
Changes:
- Add
IMap.ShowInfoWindow(IMapPin)/IMap.HideInfoWindow(IMapPin)and handler command mappings, with Android + iOS/MacCatalyst implementations. - Add
Pin.ShowInfoWindow()/Pin.HideInfoWindow()and fixPin.Parentassignment when pins are added/removed fromMap.Pins. - Add unit tests and a new sample gallery page demonstrating the feature.
Reviewed changes
Copilot reviewed 28 out of 28 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Core/maps/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt | Adds new Core Maps public APIs for info window show/hide + handler command methods. |
| src/Core/maps/src/PublicAPI/net/PublicAPI.Unshipped.txt | Adds new Core Maps public APIs for info window show/hide + handler command methods. |
| src/Core/maps/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt | Adds new Core Maps public APIs for info window show/hide + handler command methods. |
| src/Core/maps/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt | Adds new Core Maps public APIs for info window show/hide + handler command methods. |
| src/Core/maps/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt | Adds new Core Maps public APIs for info window show/hide + handler command methods. |
| src/Core/maps/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt | Adds new Core Maps public APIs for info window show/hide + handler command methods. |
| src/Core/maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txt | Adds new Core Maps public APIs for info window show/hide + handler command methods. |
| src/Core/maps/src/Core/IMap.cs | Extends IMap with ShowInfoWindow/HideInfoWindow methods. |
| src/Core/maps/src/Handlers/Map/MapHandler.cs | Adds command mapper entries and command handlers for show/hide info window. |
| src/Core/maps/src/Handlers/Map/MapHandler.Android.cs | Implements show/hide via marker lookup + Marker.ShowInfoWindow() / HideInfoWindow(). |
| src/Core/maps/src/Handlers/Map/MapHandler.iOS.cs | Implements show/hide via SelectAnnotation / DeselectAnnotation. |
| src/Core/maps/src/Handlers/Map/MapHandler.Windows.cs | Adds stub instance methods for show/hide on unsupported platform handler. |
| src/Core/maps/src/Handlers/Map/MapHandler.Tizen.cs | Adds stub instance methods for show/hide on unsupported platform handler. |
| src/Core/maps/src/Handlers/Map/MapHandler.Standard.cs | Adds stub instance methods for show/hide on unsupported platform handler. |
| src/Controls/Maps/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt | Adds new Pin.ShowInfoWindow() / Pin.HideInfoWindow() public APIs. |
| src/Controls/Maps/src/PublicAPI/net/PublicAPI.Unshipped.txt | Adds new Pin.ShowInfoWindow() / Pin.HideInfoWindow() public APIs. |
| src/Controls/Maps/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt | Adds new Pin.ShowInfoWindow() / Pin.HideInfoWindow() public APIs. |
| src/Controls/Maps/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt | Adds new Pin.ShowInfoWindow() / Pin.HideInfoWindow() public APIs. |
| src/Controls/Maps/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt | Adds new Pin.ShowInfoWindow() / Pin.HideInfoWindow() public APIs. |
| src/Controls/Maps/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt | Adds new Pin.ShowInfoWindow() / Pin.HideInfoWindow() public APIs. |
| src/Controls/Maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txt | Adds new Pin.ShowInfoWindow() / Pin.HideInfoWindow() public APIs. |
| src/Controls/Maps/src/Pin.cs | Adds ShowInfoWindow() / HideInfoWindow() convenience methods that invoke map handler commands. |
| src/Controls/Maps/src/Map.cs | Fixes Pin.Parent assignment on pin add/remove (collection changed handler). |
| src/Controls/Maps/src/HandlerImpl/Map.Impl.cs | Implements new IMap interface methods by invoking handler commands. |
| src/Controls/tests/Core.UnitTests/MapTests.cs | Adds unit tests to ensure show/hide calls don’t throw. |
| src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapsGallery.cs | Adds navigation entry to the new “Info Window” sample. |
| src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/InfoWindowGallery.xaml | New sample UI for showing/hiding info windows. |
| src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/InfoWindowGallery.xaml.cs | New sample code-behind that calls Pin.ShowInfoWindow() / HideInfoWindow(). |
src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapsGallery.cs
Outdated
Show resolved
Hide resolved
src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/InfoWindowGallery.xaml
Outdated
Show resolved
Hide resolved
| if (e.NewItems is not null) | ||
| { | ||
| foreach (Pin pin in e.NewItems) | ||
| { | ||
| pin.Parent = this; | ||
| } | ||
| } | ||
|
|
||
| if (e.OldItems is not null) | ||
| { | ||
| foreach (Pin pin in e.OldItems) | ||
| { | ||
| pin.Parent = null; | ||
| } | ||
| } |
There was a problem hiding this comment.
PinsOnCollectionChanged sets pin.Parent for NewItems and clears it for OldItems, but ObservableCollection.Clear() raises a Reset action with no OldItems. That means map.Pins.Clear() (and other reset scenarios) will leave the removed pins' Parent pointing at the Map, which can cause incorrect behavior and keep the Map alive (leak) if pins are retained elsewhere. Consider handling NotifyCollectionChangedAction.Reset (e.g., by using a custom collection that raises a Remove event with the removed items, similar to GestureElement.GestureRecognizerCollection).
There was a problem hiding this comment.
I think it looks like a good point. I've seen some devs reporting issues with memory leaks when it comes to map, so it would be good to make sure we avoid them
There was a problem hiding this comment.
This is a valid concern but it's a pre-existing issue in Map.cs, not introduced by this PR. The Reset action not clearing Parent was already the behavior before this change. A separate issue/PR should address this collection management improvement.
75cc838 to
72d68d4
Compare
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
Independent Code ReviewSummary: Adds programmatic ✅ What Looks Good
🔴 Issues1. Android 2. 3. Windows/Tizen/Standard throw 4. Double invocation risk 🟡 Nits
|
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 33985Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 33985" |
|
Thanks for the review @kubaflo! Fixed in latest commit:
Responses to other points: 1. Android GetMarkerForPin linear scan — True, it's O(n). However, maps typically have < 100 pins, making this negligible. A \Dictionary<string, Marker>\ would require maintaining the dictionary lifecycle in sync with marker add/remove. The current approach is simple and correct. If performance becomes an issue with large pin counts, it can be optimized later. 2. Pin.Parent Reset action — As I noted in the review thread, this is a pre-existing issue. The \PinsOnCollectionChanged\ method never handled \Reset\ before this PR, and this PR only adds the \NewItems/\OldItems\ handling for \Parent. A separate PR should address \Reset\ handling across all Map collections. Filed issue to track. 3. Double invocation risk — The explicit \IMap.ShowInfoWindow\ implementation in \Map.Impl.cs\ and the \Pin.ShowInfoWindow()\ method both call \Handler.Invoke, but they serve different purposes: \IMap\ is called by the handler infrastructure, \Pin\ is the public API. There's no risk of double invocation because users only call \Pin.ShowInfoWindow()\ — the \IMap\ interface method is for handler plumbing. 4. Pin.Parent in separate PR — Agreed it could have been separate, but it was a required fix for \ShowInfoWindow\ to work (the Pin needs to traverse to its parent Map). Including it here makes the feature complete and self-contained. |
🔍 Round 2 Review — PR #33985 (ShowInfoWindow / HideInfoWindow)Updated with author response analysis ✅ Fixed Since Round 1
|
|
/azp run maui-pr |
|
Azure Pipelines successfully started running 1 pipeline(s). |
📋 Round 3 — PR #33985 (ShowInfoWindow / HideInfoWindow) — Final RecommendationNo outstanding issues. Pre-existing Recommendation: Merge when CI passes. The Merge order note: This PR depends on |
a5a22d4 to
31a6238
Compare
Adds the ability to programmatically show and hide info windows on map pins. This is a frequently requested feature (issue #10601) that enables developers to control pin callout visibility from code. Implementation: - Added ShowInfoWindow(IMapPin) and HideInfoWindow(IMapPin) to IMap interface - Added ShowInfoWindow() and HideInfoWindow() public methods on Pin class - Android: Uses Marker.ShowInfoWindow()/HideInfoWindow() via marker lookup - iOS: Uses MKMapView.SelectAnnotation()/DeselectAnnotation() with animation - Added stubs for Standard, Windows, and Tizen handlers - Set Pin.Parent when pins are added/removed from Map.Pins collection - Updated all PublicAPI.Unshipped.txt files (14 files) - Added 4 unit tests (all 33 map tests pass) - Added InfoWindowGallery sample page Fixes #10601
- Fix namespace mismatch: InfoWindowGallery uses correct namespace - Fix x:Class in XAML to match corrected namespace - Fix MapsGallery.cs reference to InfoWindowGallery (remove extra qualification) - Remove duplicate PublicAPI entries (keep nullable-annotated versions only)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of throwing NotImplementedException, these methods now silently no-op on Standard, Tizen, and Windows platforms where info window control is not supported. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
31a6238 to
936b853
Compare
|
🚨 API change(s) detected @davidortinau FYI |
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
Adds the ability to programmatically show and hide map pin info windows/callouts, addressing a long-standing community request.
Fixes #10601
API Changes
Microsoft.Maui.Maps.IMap(Core):void ShowInfoWindow(IMapPin pin)— Shows the info window for the specified pinvoid HideInfoWindow(IMapPin pin)— Hides the info window for the specified pinMicrosoft.Maui.Controls.Maps.Pin(Controls):void ShowInfoWindow()— Shows the info window for this pinvoid HideInfoWindow()— Hides the info window for this pinImplementation Details
Marker.ShowInfoWindow()/Marker.HideInfoWindow()via Google Maps SDKMKMapView.SelectAnnotation()/MKMapView.DeselectAnnotation()via MapKitBug Fix: Pin.Parent tracking
Fixed a pre-existing bug where
Pin.Parentwas never set when pins were added toMap.Pins. ThePinsOnCollectionChangedmethod now properly setspin.Parent = thisfor new pins andpin.Parent = nullfor removed pins. This fix is required forShowInfoWindow()to work (it needs to traverse to the parent Map to invoke the handler), but also fixes any other code path that relies onPin.Parent.Testing
Part of Maps Epic: #33787