Zdieľať cez


Xamarin.Forms Map Pins

The Xamarin.Forms Map control allows locations to be marked with Pin objects. A Pin is a map marker that opens an information window when tapped:

Screenshot of a map pin and its information window, on iOS and Android

When a Pin object is added to the Map.Pins collection, the pin is rendered on the map.

The Pin class has the following properties:

  • Address, of type string, which typically represents the address for the pin location. However, it can be any string content, not just an address.
  • Label, of type string, which typically represents the pin title.
  • Position, of type Position, which represents the latitude and longitude of the pin.
  • Type, of type PinType, which represents the type of pin.

These properties are backed by BindableProperty objects, which means a Pin can be the target of data bindings. For more information about data binding Pin objects, see Display a pin collection.

In addition, the Pin class defines MarkerClicked and InfoWindowClicked events. The MarkerClicked event is fired when a pin is tapped, and the InfoWindowClicked event is fired when the information window is tapped. The PinClickedEventArgs object that accompanies both events has a single HideInfoWindow property, of type bool.

Display a pin

A Pin can be added to a Map in XAML:

<ContentPage ...
             xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps">
     <maps:Map x:Name="map"
               IsShowingUser="True"
               MoveToLastRegionOnLayoutChange="False">
         <x:Arguments>
             <maps:MapSpan>
                 <x:Arguments>
                     <maps:Position>
                         <x:Arguments>
                             <x:Double>36.9628066</x:Double>
                             <x:Double>-122.0194722</x:Double>
                         </x:Arguments>
                     </maps:Position>
                     <x:Double>0.01</x:Double>
                     <x:Double>0.01</x:Double>
                 </x:Arguments>
             </maps:MapSpan>
         </x:Arguments>
         <maps:Map.Pins>
             <maps:Pin Label="Santa Cruz"
                       Address="The city with a boardwalk"
                       Type="Place">
                 <maps:Pin.Position>
                     <maps:Position>
                         <x:Arguments>
                             <x:Double>36.9628066</x:Double>
                             <x:Double>-122.0194722</x:Double>
                         </x:Arguments>
                     </maps:Position>
                 </maps:Pin.Position>
             </maps:Pin>
         </maps:Map.Pins>
     </maps:Map>
</ContentPage>

This XAML creates a Map object that shows the region that is specified by the MapSpan object. The MapSpan object is centered on the latitude and longitude represented by a Position object, which extends 0.01 latitude and longitude degrees. A Pin object is added to the Map.Pins collection, and drawn on the Map at the location specified by its Position property. For information about the Position struct, see Map Position and Distance. For information about passing arguments in XAML to objects that lack default constructors, see Passing Arguments in XAML.

The equivalent C# code is:

using Xamarin.Forms.Maps;
// ...
Map map = new Map
{
  // ...
};
Pin pin = new Pin
{
  Label = "Santa Cruz",
  Address = "The city with a boardwalk",
  Type = PinType.Place,
  Position = new Position(36.9628066, -122.0194722)
};
map.Pins.Add(pin);

Warning

Failure to set the Pin.Label property will result in an ArgumentException being thrown when the Pin is added to a Map.

This example code results in a single pin being rendered on a map:

Screenshot of a map pin, on iOS and Android

Interact with a pin

By default, when a Pin is tapped its information window is displayed:

Screenshot of a map pin and its information window, on iOS and Android

Tapping elsewhere on the map closes the information window.

The Pin class defines a MarkerClicked event, which is fired when a Pin is tapped. It's not necessary to handle this event to display the information window. Instead, this event should be handled when there's a requirement to be notified that a specific pin has been tapped.

The Pin class also defines a InfoWindowClicked event that's fired when an information window is tapped. This event should be handled when there's a requirement to be notified that a specific information window has been tapped.

The following code shows an example of handling these events:

using Xamarin.Forms.Maps;
// ...
Pin boardwalkPin = new Pin
{
    Position = new Position(36.9641949, -122.0177232),
    Label = "Boardwalk",
    Address = "Santa Cruz",
    Type = PinType.Place
};
boardwalkPin.MarkerClicked += async (s, args) =>
{
    args.HideInfoWindow = true;
    string pinName = ((Pin)s).Label;
    await DisplayAlert("Pin Clicked", $"{pinName} was clicked.", "Ok");
};

Pin wharfPin = new Pin
{
    Position = new Position(36.9571571, -122.0173544),
    Label = "Wharf",
    Address = "Santa Cruz",
    Type = PinType.Place
};
wharfPin.InfoWindowClicked += async (s, args) =>
{
    string pinName = ((Pin)s).Label;
    await DisplayAlert("Info Window Clicked", $"The info window was clicked for {pinName}.", "Ok");
};

The PinClickedEventArgs object that accompanies both events has a single HideInfoWindow property, of type bool. When this property is set to true inside an event handler, the information window will be hidden.

Pin types

Pin objects include a Type property, of type PinType, which represents the type of pin. The PinType enumeration defines the following members:

  • Generic, represents a generic pin.
  • Place, represents a pin for a place.
  • SavedPin, represents a pin for a saved location.
  • SearchResult, represents a pin for a search result.

However, setting the Pin.Type property to any PinType member does not change the appearance of the rendered pin. Instead, you must create a custom renderer to customize pin appearance. For more information, see Customizing a map pin.

Display a pin collection

The Map class defines the following properties:

Important

The ItemTemplate property takes precedence when both the ItemTemplate and ItemTemplateSelector properties are set.

A Map can be populated with pins by using data binding to bind its ItemsSource property to an IEnumerable collection:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"
             x:Class="WorkingWithMaps.PinItemsSourcePage">
    <Grid>
        ...
        <maps:Map x:Name="map"
                  ItemsSource="{Binding Locations}">
            <maps:Map.ItemTemplate>
                <DataTemplate>
                    <maps:Pin Position="{Binding Position}"
                              Address="{Binding Address}"
                              Label="{Binding Description}" />
                </DataTemplate>
            </maps:Map.ItemTemplate>
        </maps:Map>
        ...
    </Grid>
</ContentPage>

The ItemsSource property data binds to the Locations property of the connected viewmodel, which returns an ObservableCollection of Location objects, which is a custom type. Each Location object defines Address and Description properties, of type string, and a Position property, of type Position.

The appearance of each item in the IEnumerable collection is defined by setting the ItemTemplate property to a DataTemplate that contains a Pin object that data binds to appropriate properties.

The following screenshots show a Map displaying a Pin collection using data binding:

Screenshot of map with data bound pins, on iOS and Android

Choose item appearance at runtime

The appearance of each item in the IEnumerable collection can be chosen at runtime, based on the item value, by setting the ItemTemplateSelector property to a DataTemplateSelector:

<ContentPage ...
             xmlns:local="clr-namespace:WorkingWithMaps"
             xmlns:maps="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps">
    <ContentPage.Resources>
        <local:MapItemTemplateSelector x:Key="MapItemTemplateSelector">
            <local:MapItemTemplateSelector.DefaultTemplate>
                <DataTemplate>
                    <maps:Pin Position="{Binding Position}"
                              Address="{Binding Address}"
                              Label="{Binding Description}" />
                </DataTemplate>
            </local:MapItemTemplateSelector.DefaultTemplate>
            <local:MapItemTemplateSelector.XamarinTemplate>
                <DataTemplate>
                    <!-- Change the property values, or the properties that are bound to. -->
                    <maps:Pin Position="{Binding Position}"
                              Address="{Binding Address}"
                              Label="Xamarin!" />
                </DataTemplate>
            </local:MapItemTemplateSelector.XamarinTemplate>    
        </local:MapItemTemplateSelector>
    </ContentPage.Resources>

    <Grid>
        ...
        <maps:Map x:Name="map"
                  ItemsSource="{Binding Locations}"
                  ItemTemplateSelector="{StaticResource MapItemTemplateSelector}" />
        ...
    </Grid>
</ContentPage>

The following example shows the MapItemTemplateSelector class:

public class MapItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate DefaultTemplate { get; set; }
    public DataTemplate XamarinTemplate { get; set; }

    protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
    {
        return ((Location)item).Address.Contains("San Francisco") ? XamarinTemplate : DefaultTemplate;
    }
}

The MapItemTemplateSelector class defines DefaultTemplate and XamarinTemplate DataTemplate properties that are set to different data templates. The OnSelectTemplate method returns the XamarinTemplate, which displays "Xamarin" as a label when a Pin is tapped, when the item has an address that contains "San Francisco". When the item doesn't have an address that contains "San Francisco", the OnSelectTemplate method returns the DefaultTemplate.

Note

A use case for this functionality is binding properties of sub-classed Pin objects to different properties, based on the Pin sub-type.

For more information about data template selectors, see Creating a Xamarin.Forms DataTemplateSelector.