Skip to content

Add MapStyle property for custom JSON map styling (Android)#33991

Merged
jfversluis merged 5 commits intonet11.0from
feature/custom-map-styles
Mar 3, 2026
Merged

Add MapStyle property for custom JSON map styling (Android)#33991
jfversluis merged 5 commits intonet11.0from
feature/custom-map-styles

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

Adds a MapStyle property to the Map control that accepts a Google Maps JSON style string for customizing the visual appearance of the map on Android.

Fixes #10706

What this does

  • Adds IMap.MapStyle (string?) property to the core map interface
  • Adds Map.MapStyleProperty BindableProperty on the Controls layer
  • Android: Applies the JSON style via GoogleMap.SetMapStyle(new MapStyleOptions(json))
  • iOS/MacCatalyst/Windows: Marked with [UnsupportedOSPlatform] — Apple MapKit does not support custom JSON map styling (only dark/light mode), so this property is Android-only

Why Android-only?

Apple MapKit has no equivalent to Google Maps' SetMapStyle(JSON) API. Rather than providing a misleading partial implementation (e.g., only toggling dark/light mode based on JSON analysis), the property is marked with [UnsupportedOSPlatform("ios")], [UnsupportedOSPlatform("maccatalyst")], and [UnsupportedOSPlatform("windows")]. This gives developers a compile-time CA1416 warning when targeting unsupported platforms.

Changes

  • IMap.cs — Added MapStyle property with [UnsupportedOSPlatform] attributes
  • Map.cs — Added MapStyleProperty BindableProperty with [UnsupportedOSPlatform] attributes
  • MapHandler.cs — Added PropertyMapper entry
  • MapHandler.Android.cs — Calls UpdateMapStyle on the native GoogleMap
  • MapHandler.iOS.cs — No-op handler with comment
  • MapExtensions.cs (Android) — UpdateMapStyle() extension method
  • All handler stubs (Standard, Windows, Tizen)
  • 14 PublicAPI.Unshipped.txt files updated
  • 3 unit tests added
  • MapStyleGallery sample page with Dark, Retro, and Night presets

Verified

  • ✅ Android: All 4 styles (Default, Dark, Retro, Night) visually verified
  • ✅ iOS: Build succeeds, no CA1416 warnings
  • ✅ Unit tests: 32/32 map tests pass

Part of the Maps Improvements Epic: #33787

Copilot AI review requested due to automatic review settings February 11, 2026 12:36
@jfversluis jfversluis added this to the .NET 11.0-preview1 milestone Feb 11, 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 an Android-only MapStyle API to the MAUI Maps control surface so developers can apply Google Maps JSON styling, wiring it through the handler/property mapper, updating PublicAPI files, and providing basic unit coverage + a sample gallery page.

Changes:

  • Introduces IMap.MapStyle and Map.MapStyle (BindableProperty) and maps it through MapHandler.
  • Android implementation applies the JSON string using GoogleMap.SetMapStyle(new MapStyleOptions(json)); other platforms add stubs/no-ops.
  • Updates PublicAPI.Unshipped files, adds unit tests, and adds a sample “Map Style” gallery page.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Core/maps/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt Declares new IMap.MapStyle and handler mapper API for netstandard.
src/Core/maps/src/PublicAPI/net/PublicAPI.Unshipped.txt Declares new IMap.MapStyle and handler mapper API for net.
src/Core/maps/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt Declares new IMap.MapStyle and handler mapper API for Windows TFM.
src/Core/maps/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt Declares new IMap.MapStyle and handler mapper API for Tizen TFM.
src/Core/maps/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Declares new IMap.MapStyle and handler mapper API for MacCatalyst TFM.
src/Core/maps/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt Declares new IMap.MapStyle and handler mapper API for iOS TFM.
src/Core/maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txt Declares new IMap.MapStyle, handler mapper API, and Android UpdateMapStyle extension API.
src/Core/maps/src/Platform/Android/MapExtensions.cs Adds UpdateMapStyle extension to apply/clear Google Maps JSON styling.
src/Core/maps/src/Handlers/Map/MapHandler.iOS.cs Adds a no-op MapMapStyle mapping method for iOS/MacCatalyst.
src/Core/maps/src/Handlers/Map/MapHandler.cs Adds MapStyle entry to the handler property mapper.
src/Core/maps/src/Handlers/Map/MapHandler.Windows.cs Adds MapMapStyle stub for Windows handler.
src/Core/maps/src/Handlers/Map/MapHandler.Tizen.cs Adds MapMapStyle stub for Tizen handler.
src/Core/maps/src/Handlers/Map/MapHandler.Standard.cs Adds MapMapStyle stub for standard/netstandard handler.
src/Core/maps/src/Handlers/Map/MapHandler.Android.cs Wires MapMapStyle and applies initial style in OnMapReady.
src/Core/maps/src/Core/IMap.cs Adds MapStyle to the core map interface with platform annotations.
src/Controls/tests/Core.UnitTests/MapTests.cs Adds unit tests for default/set/clear behavior of Map.MapStyle.
src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapsGallery.cs Adds navigation entry to the new Map Style sample page.
src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapStyleGallery.xaml.cs Adds code-behind to switch between default/dark/retro/night JSON styles.
src/Controls/samples/Controls.Sample/Pages/Controls/MapsGalleries/MapStyleGallery.xaml Adds UI for the Map Style sample page.
src/Controls/Maps/src/PublicAPI/netstandard/PublicAPI.Unshipped.txt Declares new Map.MapStyle APIs for netstandard.
src/Controls/Maps/src/PublicAPI/net/PublicAPI.Unshipped.txt Declares new Map.MapStyle APIs for net.
src/Controls/Maps/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt Declares new Map.MapStyle APIs for Windows TFM.
src/Controls/Maps/src/PublicAPI/net-tizen/PublicAPI.Unshipped.txt Declares new Map.MapStyle APIs for Tizen TFM.
src/Controls/Maps/src/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Declares new Map.MapStyle APIs for MacCatalyst TFM.
src/Controls/Maps/src/PublicAPI/net-ios/PublicAPI.Unshipped.txt Declares new Map.MapStyle APIs for iOS TFM.
src/Controls/Maps/src/PublicAPI/net-android/PublicAPI.Unshipped.txt Declares new Map.MapStyle APIs for Android TFM.
src/Controls/Maps/src/Map.cs Adds MapStyleProperty and the MapStyle bindable property with platform annotations.

Comment thread src/Core/maps/src/Core/IMap.cs
Comment thread src/Controls/Maps/src/Map.cs
Comment thread src/Core/maps/src/Platform/Android/MapExtensions.cs
@jfversluis
Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Feb 26, 2026

Independent Code Review

Summary: Adds a MapStyle string property to IMap/Map for applying Google Maps JSON styling on Android, with no-op implementations on iOS/MacCatalyst/Windows/Tizen. Includes a sample gallery page and 3 unit tests. Closes #10706.

✅ What Looks Good

  • Correct handler plumbing — Property mapper registration in MapHandler.cs, platform-specific handler methods, and OnMapReady initialization are all wired up properly
  • Good use of [UnsupportedOSPlatform] with #if !NETSTANDARD guards to give compile-time CA1416 warnings
  • Android implementation is solid — Null-check on googleMap, SetMapStyle(null) to clear, and checking the return value of SetMapStyle
  • PublicAPI files all updated consistently

🔴 Issues

1. Error handling uses Debug.WriteLine — silent failure in production

if (!result)
{
    System.Diagnostics.Debug.WriteLine("Failed to apply map style...");
}

Debug.WriteLine is stripped in Release builds. Invalid JSON will silently fail with no feedback. This should use ILogger (already available via MauiContext) or at minimum throw so developers know their style JSON is malformed.

2. MapStyleOptions is never disposed
MapStyleOptions inherits from Java.Lang.Object which implements IDisposable. This should be wrapped in a using statement to avoid leaking a JNI reference:

using var styleOptions = new MapStyleOptions(map.MapStyle);

3. Non-Android platform handlers throw NotImplementedException
Standard/Tizen/Windows all throw new NotImplementedException() for MapMapStyle. If a developer writes cross-platform code that sets MapStyle, the iOS handler correctly no-ops, but Windows will crash at runtime. The iOS approach (empty method body) should be used consistently.

🟡 Suggestions

  • Sample code has verbose #pragma warning disable CA1416 in every button handler — consider suppressing once at file level or using OperatingSystem.IsAndroid() checks instead (better example for users)
  • No JSON validation on set — consider basic validation (e.g., checking the string starts with [)
  • Missing test for PropertyChanged notification when MapStyle changes

@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 -- 33991

Or

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

@jfversluis
Copy link
Copy Markdown
Member Author

Review Response

Thanks for the thorough review @kubaflo! All three red issues have been addressed in the latest commit:

Issues - All Fixed

1. Debug.WriteLine to ILogger Fixed.
Replaced Debug.WriteLine with ILogger resolved via MauiContext.Services. The UpdateMapStyle signature now accepts an optional IMauiContext? parameter (following the same pattern as UpdateIsShowingUser). Both call sites in MapHandler.Android.cs now pass MauiContext.

2. MapStyleOptions disposal Fixed.
Added using var styleOptions = new MapStyleOptions(map.MapStyle); to properly dispose the JNI reference after SetMapStyle returns.

3. NotImplementedException to no-op Fixed.
Changed Standard, Tizen, and Windows handlers from throw new NotImplementedException() to empty method bodies { }, consistent with the iOS handler approach. Cross-platform code that sets MapStyle will now silently no-op on unsupported platforms instead of crashing.

Suggestions - Responses

  • Sample pragma warnings: Good point - the per-method pragma warning disable CA1416 is verbose. However, file-level suppression would suppress the warning for the entire file which could mask legitimate issues. The current approach, while verbose, is explicit. OperatingSystem.IsAndroid() checks would be cleaner for a sample but add more code. Happy to refactor if preferred.

  • JSON validation on set: Intentionally omitted - SetMapStyle already validates and returns false for invalid JSON. Adding our own validation would duplicate Google's parser and could reject valid styles that use features we don't anticipate. The ILogger warning now surfaces failures to developers.

  • PropertyChanged test: Good catch. The existing MapStyleIsSetCorrectly test verifies the property value, and MapStyle is a standard BindableProperty which inherits PropertyChanged notification from the framework. Adding an explicit test is possible but would be testing BindableProperty infrastructure rather than MapStyle-specific logic.

All changes verified: Build passes, 35 map tests pass.

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Feb 27, 2026

🔍 Round 2 Review — PR #33991 (MapStyle)

Updated with author response analysis

✅ Fixed Since Round 1

  • MapStyleOptions disposal: using var styleOptions = new MapStyleOptions(map.MapStyle) — JNI object leak fixed. ✅
  • Error logging: Uses ILogger.LogWarning instead of Debug.WriteLine — works in Release builds. ✅
  • Non-Android handlers: Standard, Tizen, Windows, and iOS all use empty method bodies { }. ✅
  • [UnsupportedOSPlatform] attributes: Compile-time warnings for iOS, macCatalyst, Windows. ✅
  • MapStyle applied on OnMapReady: Style applied during initial map setup. ✅

⚠️ Issues & Author Responses

1. UpdateMapStyle is a public extension methodMinor, not a blocker
This follows the existing MAUI Maps pattern (UpdateIsTrafficEnabled, UpdateIsScrollEnabled are also public extension methods). Consistent with the codebase.

2. MapStyle property has no validationRetracted
Author correctly explains: GoogleMap.SetMapStyle already validates the JSON and returns false for invalid styles. Adding our own validation would duplicate Google's parser and could reject valid styles using features we don't anticipate. The ILogger warning now surfaces failures. This is the right approach — validate at the platform layer, not the abstraction layer. ✅

3. iOS silent no-opRetracted
The [UnsupportedOSPlatform] attributes provide compile-time warnings, which is the .NET standard mechanism for platform-specific APIs. Consumers get warned at build time. The empty handler body is the correct behavior. ✅

4. Sample pragma warningsAcknowledged as intentional
Author explains per-method #pragma warning disable CA1416 is verbose but explicit — file-level suppression could mask legitimate issues. Valid reasoning. ✅

👍 What Looks Good

  • UpdateMapStyle follows existing MAUI extension method patterns.
  • Clean separation: handler delegates to extension method for testability.
  • Setting MapStyle = null correctly reverts to default.
  • BindableProperty enables XAML binding and runtime style switching.
  • Comprehensive tests including null, valid JSON, invalid JSON, and PropertyChanged.

Summary

All Round 1 critical issues were fixed, and all Round 2 concerns were satisfactorily addressed. The author's point about delegating JSON validation to the platform layer (Google's parser) is particularly well-reasoned. This is the most merge-ready PR of the 8.

@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 27, 2026

📋 Round 3 — PR #33991 (MapStyle) — Final Recommendation

No outstanding issues. This was the most polished PR throughout the review process.

Recommendation: Merge when CI passes. This is the simplest and cleanest of the 8 PRs — good candidate to merge first (least conflict risk).


🗺️ Suggested Merge Order for All 8 PRs

To minimize rebase conflicts (all 8 PRs modify MapsGallery.cs, IMap.cs, and PublicAPI files):

  1. Add MapStyle property for custom JSON map styling (Android) #33991 (MapStyle) — Smallest footprint, fewest file overlaps
  2. Add MapLongClicked event to Map control #33792 (MapLongClicked) — Small, self-contained
  3. Add UserLocationChanged event and LastUserLocation property to Map #33799 (UserLocationChanged) — Re-run iOS CoreCLR device tests first
  4. Add MoveToRegion animation control, MapSpan.FromLocations, and fix initial map animation #33982 (MoveToRegion animated) — Introduces MoveToRegionRequest other PRs don't depend on
  5. Add Pin.ShowInfoWindow() and Pin.HideInfoWindow() methods #33985 (ShowInfoWindow) — Adds Pin.Parent assignment needed by clustering
  6. Add Custom Pin Icons support for Maps #33950 (Custom Pin Icons) — Modifies AddPins async flow
  7. Add Pin Clustering support for Maps #33831 (Pin Clustering) — Largest map PR, most interface changes
  8. Add LongPressGestureRecognizer for .NET MAUI 11 #33432 (LongPressGesture) — Separate from Maps, can merge independently at any point

kubaflo
kubaflo previously approved these changes Feb 27, 2026
@kubaflo kubaflo enabled auto-merge (squash) February 27, 2026 13:37
@jfversluis jfversluis force-pushed the feature/custom-map-styles branch 4 times, most recently from 07aa9d0 to 28fe50e Compare March 2, 2026 19:37
@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
jfversluis and others added 5 commits March 3, 2026 08:59
- Add MapStyle string property to IMap interface and Map control
- Android: Full Google Maps JSON styling via MapStyleOptions
- iOS/MacCatalyst/Windows: No-op with [UnsupportedOSPlatform] attributes
  (MapKit and other native map controls don't support custom JSON styling)
- Developers get compile-time CA1416 warning when using on unsupported platforms
- Add MapStyleGallery sample page with Dark, Retro, Night presets
- Add 3 unit tests for MapStyle property
- Update all PublicAPI.Unshipped.txt files

Closes #10706
- Guard UnsupportedOSPlatform attributes with #if !NETSTANDARD for netstandard TFMs
- Check SetMapStyle return value and log warning on failure
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot CLI would wrongfully come up with old pipeline names
(e.g., MAUI-UITests-public) found in historical PR comments.
This documents the current correct names so agents use them:
- maui-pr (overall CI)
- maui-pr-devicetests (device tests)
- maui-pr-uitests (UI tests)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jfversluis jfversluis force-pushed the feature/custom-map-styles branch from 28fe50e to 17c39b2 Compare March 3, 2026 08:00
@jfversluis jfversluis disabled auto-merge March 3, 2026 09:59
@jfversluis jfversluis merged commit d81b719 into net11.0 Mar 3, 2026
25 of 29 checks passed
@jfversluis jfversluis deleted the feature/custom-map-styles branch March 3, 2026 10:20
@github-actions github-actions bot locked and limited conversation to collaborators Apr 3, 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.

3 participants