Skip to content
Merged
4 changes: 4 additions & 0 deletions src/Controls/Maps/src/HandlerImpl/Pin.Impl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@ namespace Microsoft.Maui.Controls.Maps
{
public partial class Pin : IMapPin
{
/// <summary>
/// Explicit implementation of IMapPin.ImageSource to convert from ImageSource to IImageSource.
/// </summary>
IImageSource? IMapPin.ImageSource => ImageSource;
}
}
19 changes: 19 additions & 0 deletions src/Controls/Maps/src/Pin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ public partial class Pin : Element
/// <summary>Bindable property for <see cref="ClusteringIdentifier"/>.</summary>
public static readonly BindableProperty ClusteringIdentifierProperty = BindableProperty.Create(nameof(ClusteringIdentifier), typeof(string), typeof(Pin), DefaultClusteringIdentifier);

/// <summary>Bindable property for <see cref="ImageSource"/>.</summary>
public static readonly BindableProperty ImageSourceProperty = BindableProperty.Create(nameof(ImageSource), typeof(ImageSource), typeof(Pin), default(ImageSource));

private object? _markerId;

/// <inheritdoc />
Expand Down Expand Up @@ -81,6 +84,22 @@ public PinType Type
set { SetValue(TypeProperty, value); }
}

/// <summary>
/// Gets or sets the custom image source for this pin's icon.
/// When set, this image will be used instead of the default platform pin icon.
/// This is a bindable property.
/// </summary>
/// <remarks>
/// Supported image sources include file-based images, embedded resources, URIs, and streams.
/// The image will be scaled by the underlying platform to a platform-defined size (32x32 points on iOS, 64x64 pixels on Android).
/// Provide images that look clear when scaled to these sizes.
/// </remarks>
public ImageSource? ImageSource
{
get { return (ImageSource?)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}

/// <summary>
/// Gets or sets the platform counterpart of this pin element.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Microsoft.Maui.Controls.Maps.MapElement.ZIndex.get -> int
Microsoft.Maui.Controls.Maps.MapElement.ZIndex.set -> void
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.get -> string!
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.set -> void
Microsoft.Maui.Controls.Maps.Pin.ImageSource.get -> Microsoft.Maui.Controls.ImageSource?
Microsoft.Maui.Controls.Maps.Pin.ImageSource.set -> void
Microsoft.Maui.Controls.Maps.Polygon.PolygonClicked -> System.EventHandler?
Microsoft.Maui.Controls.Maps.Polyline.PolylineClicked -> System.EventHandler?
const Microsoft.Maui.Controls.Maps.Pin.DefaultClusteringIdentifier = "maui_default_cluster" -> string!
Expand All @@ -26,3 +28,4 @@ static readonly Microsoft.Maui.Controls.Maps.Map.RegionProperty -> Microsoft.Mau
static readonly Microsoft.Maui.Controls.Maps.MapElement.IsVisibleProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.MapElement.ZIndexProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifierProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ImageSourceProperty -> Microsoft.Maui.Controls.BindableProperty!
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Microsoft.Maui.Controls.Maps.MapElement.ZIndex.get -> int
Microsoft.Maui.Controls.Maps.MapElement.ZIndex.set -> void
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.get -> string!
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.set -> void
Microsoft.Maui.Controls.Maps.Pin.ImageSource.get -> Microsoft.Maui.Controls.ImageSource?
Microsoft.Maui.Controls.Maps.Pin.ImageSource.set -> void
Microsoft.Maui.Controls.Maps.Polygon.PolygonClicked -> System.EventHandler?
Microsoft.Maui.Controls.Maps.Polyline.PolylineClicked -> System.EventHandler?
const Microsoft.Maui.Controls.Maps.Pin.DefaultClusteringIdentifier = "maui_default_cluster" -> string!
Expand All @@ -26,3 +28,4 @@ static readonly Microsoft.Maui.Controls.Maps.Map.RegionProperty -> Microsoft.Mau
static readonly Microsoft.Maui.Controls.Maps.MapElement.IsVisibleProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.MapElement.ZIndexProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifierProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ImageSourceProperty -> Microsoft.Maui.Controls.BindableProperty!
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Microsoft.Maui.Controls.Maps.MapElement.ZIndex.get -> int
Microsoft.Maui.Controls.Maps.MapElement.ZIndex.set -> void
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.get -> string!
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.set -> void
Microsoft.Maui.Controls.Maps.Pin.ImageSource.get -> Microsoft.Maui.Controls.ImageSource?
Microsoft.Maui.Controls.Maps.Pin.ImageSource.set -> void
Microsoft.Maui.Controls.Maps.Polygon.PolygonClicked -> System.EventHandler?
Microsoft.Maui.Controls.Maps.Polyline.PolylineClicked -> System.EventHandler?
const Microsoft.Maui.Controls.Maps.Pin.DefaultClusteringIdentifier = "maui_default_cluster" -> string!
Expand All @@ -26,3 +28,4 @@ static readonly Microsoft.Maui.Controls.Maps.Map.RegionProperty -> Microsoft.Mau
static readonly Microsoft.Maui.Controls.Maps.MapElement.IsVisibleProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.MapElement.ZIndexProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifierProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ImageSourceProperty -> Microsoft.Maui.Controls.BindableProperty!
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Microsoft.Maui.Controls.Maps.MapElement.ZIndex.get -> int
Microsoft.Maui.Controls.Maps.MapElement.ZIndex.set -> void
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.get -> string!
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.set -> void
Microsoft.Maui.Controls.Maps.Pin.ImageSource.get -> Microsoft.Maui.Controls.ImageSource?
Microsoft.Maui.Controls.Maps.Pin.ImageSource.set -> void
Microsoft.Maui.Controls.Maps.Polygon.PolygonClicked -> System.EventHandler?
Microsoft.Maui.Controls.Maps.Polyline.PolylineClicked -> System.EventHandler?
const Microsoft.Maui.Controls.Maps.Pin.DefaultClusteringIdentifier = "maui_default_cluster" -> string!
Expand All @@ -26,3 +28,4 @@ static readonly Microsoft.Maui.Controls.Maps.Map.RegionProperty -> Microsoft.Mau
static readonly Microsoft.Maui.Controls.Maps.MapElement.IsVisibleProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.MapElement.ZIndexProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifierProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ImageSourceProperty -> Microsoft.Maui.Controls.BindableProperty!
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Microsoft.Maui.Controls.Maps.MapElement.ZIndex.get -> int
Microsoft.Maui.Controls.Maps.MapElement.ZIndex.set -> void
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.get -> string!
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.set -> void
Microsoft.Maui.Controls.Maps.Pin.ImageSource.get -> Microsoft.Maui.Controls.ImageSource?
Microsoft.Maui.Controls.Maps.Pin.ImageSource.set -> void
Microsoft.Maui.Controls.Maps.Polygon.PolygonClicked -> System.EventHandler?
Microsoft.Maui.Controls.Maps.Polyline.PolylineClicked -> System.EventHandler?
const Microsoft.Maui.Controls.Maps.Pin.DefaultClusteringIdentifier = "maui_default_cluster" -> string!
Expand All @@ -26,3 +28,4 @@ static readonly Microsoft.Maui.Controls.Maps.Map.RegionProperty -> Microsoft.Mau
static readonly Microsoft.Maui.Controls.Maps.MapElement.IsVisibleProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.MapElement.ZIndexProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifierProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ImageSourceProperty -> Microsoft.Maui.Controls.BindableProperty!
3 changes: 3 additions & 0 deletions src/Controls/Maps/src/PublicAPI/net/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Microsoft.Maui.Controls.Maps.MapElement.ZIndex.get -> int
Microsoft.Maui.Controls.Maps.MapElement.ZIndex.set -> void
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.get -> string!
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.set -> void
Microsoft.Maui.Controls.Maps.Pin.ImageSource.get -> Microsoft.Maui.Controls.ImageSource?
Microsoft.Maui.Controls.Maps.Pin.ImageSource.set -> void
Microsoft.Maui.Controls.Maps.Polygon.PolygonClicked -> System.EventHandler?
Microsoft.Maui.Controls.Maps.Polyline.PolylineClicked -> System.EventHandler?
const Microsoft.Maui.Controls.Maps.Pin.DefaultClusteringIdentifier = "maui_default_cluster" -> string!
Expand All @@ -26,3 +28,4 @@ static readonly Microsoft.Maui.Controls.Maps.Map.RegionProperty -> Microsoft.Mau
static readonly Microsoft.Maui.Controls.Maps.MapElement.IsVisibleProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.MapElement.ZIndexProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifierProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ImageSourceProperty -> Microsoft.Maui.Controls.BindableProperty!
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Microsoft.Maui.Controls.Maps.MapElement.ZIndex.get -> int
Microsoft.Maui.Controls.Maps.MapElement.ZIndex.set -> void
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.get -> string!
Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifier.set -> void
Microsoft.Maui.Controls.Maps.Pin.ImageSource.get -> Microsoft.Maui.Controls.ImageSource?
Microsoft.Maui.Controls.Maps.Pin.ImageSource.set -> void
Microsoft.Maui.Controls.Maps.Polygon.PolygonClicked -> System.EventHandler?
Microsoft.Maui.Controls.Maps.Polyline.PolylineClicked -> System.EventHandler?
const Microsoft.Maui.Controls.Maps.Pin.DefaultClusteringIdentifier = "maui_default_cluster" -> string!
Expand All @@ -26,3 +28,4 @@ static readonly Microsoft.Maui.Controls.Maps.Map.RegionProperty -> Microsoft.Mau
static readonly Microsoft.Maui.Controls.Maps.MapElement.IsVisibleProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.MapElement.ZIndexProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ClusteringIdentifierProperty -> Microsoft.Maui.Controls.BindableProperty!
static readonly Microsoft.Maui.Controls.Maps.Pin.ImageSourceProperty -> Microsoft.Maui.Controls.BindableProperty!
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?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"
xmlns:maps="clr-namespace:Microsoft.Maui.Controls.Maps;assembly=Microsoft.Maui.Controls.Maps"
x:Class="Maui.Controls.Sample.Pages.MapsGalleries.CustomPinIconGallery"
Title="Custom Pin Icons">
<Grid RowDefinitions="Auto,*">
<StackLayout Orientation="Horizontal" Padding="10" Spacing="10">
<Button Text="Add Custom Pins" Clicked="OnAddCustomPinsClicked"/>
<Button Text="Add Default Pin" Clicked="OnAddDefaultPinClicked"/>
<Button Text="Clear" Clicked="OnClearClicked"/>
</StackLayout>

<maps:Map x:Name="CustomPinMap" Grid.Row="1"/>
</Grid>
</ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Maps;
using Microsoft.Maui.Devices.Sensors;
using Microsoft.Maui.Maps;

namespace Maui.Controls.Sample.Pages.MapsGalleries
{
public partial class CustomPinIconGallery : ContentPage
{
public CustomPinIconGallery()
{
InitializeComponent();

// Center on Seattle
CustomPinMap.MoveToRegion(MapSpan.FromCenterAndRadius(
new Microsoft.Maui.Devices.Sensors.Location(47.6062, -122.3321),
Distance.FromMiles(5)));
}

void OnAddCustomPinsClicked(object? sender, EventArgs e)
{
// Add pin with custom icon from an image file in the app bundle
var customPin1 = new Pin
{
Label = "Custom Icon Pin",
Address = "Using app bundle image",
Location = new Microsoft.Maui.Devices.Sensors.Location(47.6062, -122.3321),
ImageSource = ImageSource.FromFile("dotnet_bot.png")
};
CustomPinMap.Pins.Add(customPin1);

// Add another custom pin at different location
var customPin2 = new Pin
{
Label = "Another Custom Pin",
Address = "Also using custom image",
Location = new Microsoft.Maui.Devices.Sensors.Location(47.62, -122.35),
ImageSource = ImageSource.FromFile("dotnet_bot.png")
};
CustomPinMap.Pins.Add(customPin2);
}

void OnAddDefaultPinClicked(object? sender, EventArgs e)
{
// Add a regular pin without custom icon for comparison
var defaultPin = new Pin
{
Label = "Default Pin",
Address = "Standard marker icon",
Location = new Microsoft.Maui.Devices.Sensors.Location(47.59, -122.31),
Type = PinType.Place
};
CustomPinMap.Pins.Add(defaultPin);
}

void OnClearClicked(object? sender, EventArgs e)
{
CustomPinMap.Pins.Clear();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public MapsGallery()
GalleryBuilder.NavButton("Map Pins", () => new MapPinsGallery(), Navigation),
GalleryBuilder.NavButton("Pins ItemsSource", () => new PinItemsSourceGallery(), Navigation),
GalleryBuilder.NavButton("Pin Clustering", () => new ClusteringGallery(), Navigation),

GalleryBuilder.NavButton("Custom Pin Icons", () => new CustomPinIconGallery(), Navigation),
GalleryBuilder.NavButton("Circle", () => new CircleGallery(), Navigation),
GalleryBuilder.NavButton("Polygon", () => new PolygonsGallery(), Navigation),
GalleryBuilder.NavButton("Element Visibility & ZIndex", () => new MapElementVisibilityGallery(), Navigation),
Expand Down
59 changes: 59 additions & 0 deletions src/Controls/tests/Core.UnitTests/PinTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,64 @@ public void Label()

Assert.True(signaled);
}

[Fact]
public void ImageSourceDefaultsToNull()
{
var pin = new Pin();
Assert.Null(pin.ImageSource);
}

[Fact]
public void ImageSourceCanBeSet()
{
var pin = new Pin
{
ImageSource = ImageSource.FromFile("test.png")
};

Assert.NotNull(pin.ImageSource);
Assert.IsType<FileImageSource>(pin.ImageSource);
}

[Fact]
public void ImageSourcePropertyChanged()
{
var pin = new Pin();

bool signaled = false;
pin.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == "ImageSource")
signaled = true;
};

pin.ImageSource = ImageSource.FromFile("test.png");

Assert.True(signaled);
}

[Fact]
public void ImageSourceBindableProperty()
{
var pin = new Pin();
pin.SetValue(Pin.ImageSourceProperty, ImageSource.FromFile("bound.png"));

Assert.NotNull(pin.ImageSource);
Assert.IsType<FileImageSource>(pin.ImageSource);
}

[Fact]
public void IMapPinImageSourceReturnsValue()
{
var pin = new Pin
{
ImageSource = ImageSource.FromFile("test.png")
};

// Access through interface to test explicit implementation
var mapPin = (Microsoft.Maui.Maps.IMapPin)pin;
Assert.NotNull(mapPin.ImageSource);
}
}
}
6 changes: 6 additions & 0 deletions src/Core/maps/src/Core/IMapPin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public interface IMapPin : IElement
/// <remarks>This should typically not be set by the developer. Doing so might result in unpredictable behavior.</remarks>
object? MarkerId { get; set; }

/// <summary>
/// Gets the custom image source for this pin's icon.
/// </summary>
/// <remarks>When set, this image will be used instead of the default platform pin icon.</remarks>
IImageSource? ImageSource { get; }

/// <summary>
/// Sends a marker click event.
/// </summary>
Expand Down
Loading
Loading