Xamarin.Forms 地图图钉

Xamarin.FormsMap 控件允许使用 Pin 对象标记位置。 Pin 是一个地图标记,在点击时打开信息窗口:

iOS 和 Android 上的地图图钉及其信息窗口的屏幕截图

Pin 对象添加到 Map.Pins 集合中时,将在地图上呈现图钉。

Pin 类具有以下属性:

  • Address,类型为 string,通常表示图钉位置的地址。 但是,它可以是任何 string 内容,而不仅仅是地址。
  • Label 的类型为 string,通常表示图钉标题。
  • Position 的类型为 Position,表示图钉的纬度和经度。
  • Type 的类型为 PinType,表示图钉的类型。

这些属性都由 BindableProperty 对象提供支持,这意味着 Pin 可以作为数据绑定的目标。 要详细了解数据绑定 Pin 对象,请参阅显示图钉集合

此外,Pin 类还定义 MarkerClickedInfoWindowClicked 事件。 点击图钉时会触发 MarkerClicked 事件,而点击信息窗口时会触发 InfoWindowClicked 事件。 两个事件随附的 PinClickedEventArgs 对象具有一个 HideInfoWindow 属性,类型为 bool

显示图钉

可以在 XAML 中将 Pin 添加到 Map

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

此 XAML 会创建一个 Map 对象,该对象显示由 MapSpan 对象指定的区域。 MapSpan 对象以 Position 对象表示的经纬度为中心,该对象延伸 0.01 个经纬度值。 将 Pin 对象添加到 Map.Pins 集合中,并在 Map 上由其 Position 属性指定位置处绘制。 有关 Position 结构的信息,请参阅地图位置和距离。 若要了解如何在 XAML 中将自变量传递给缺少默认构造函数的对象,请参阅在 XAML 中传递自变量

等效 C# 代码如下:

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);

警告

未能设置 Pin.Label 属性会导致在将 Pin 添加到 Map 时引发 ArgumentException

此示例代码会生成在地图上呈现的单个图钉:

iOS 和 Android 上的地图图钉的屏幕截图

与图钉交互

默认情况下,点击 Pin 时,会显示其信息窗口:

iOS 和 Android 上的地图图钉及其信息窗口的屏幕截图

点击地图上的其他地方会关闭信息窗口。

Pin 类定义 MarkerClicked 事件,点击 Pin 时会触发该事件。 无需处理此事件即可显示信息窗口。 相反,当需要通知已点击特定图钉时,应处理此事件。

Pin 类还定义 InfoWindowClicked 事件,在点击信息窗口时会触发该事件。 当需要通知已点击特定信息窗口时,应处理此事件。

下方代码演示了处理这些事件的示例:

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");
};

两个事件随附的 PinClickedEventArgs 对象具有一个 HideInfoWindow 属性,类型为 bool。 当此属性在事件处理程序中设置为 true 时,信息窗口将被隐藏。

图钉类型

Pin 对象包括 Type 属性,类型为 PinType,该属性表示图钉的类型。 PinType 枚举定义以下成员:

  • Generic,表示泛型图钉。
  • Place,表示位置的图钉。
  • SavedPin,表示已保存位置的图钉。
  • SearchResult,表示搜索结果的图钉。

但是,将 Pin.Type 属性设置为任何 PinType 成员均不会更改呈现图钉的外观。 你必须改为创建自定义呈现器来自定义图钉外观。 有关详细信息,请参阅自定义地图图钉

显示 Pin 集合

Map 类定义以下属性:

重要

同时设置 ItemTemplateItemTemplateSelector 属性时,ItemTemplate 属性将优先。

可以使用图钉填充 Map,方法是使用数据绑定将其 ItemsSource 属性绑定到 IEnumerable 集合:

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

ItemsSource 属性数据绑定到连接视图模型的 Locations 属性,该属性返回 Location 对象的 ObservableCollection(一个自定义类型)。 每个 Location 对象都定义类型为 stringAddressDescription 属性,以及类型为 PositionPosition 属性。

可以定义 IEnumerable 集合中每个项的外观,方法是将 ItemTemplate 属性设置为包含数据绑定到相应属性的 Pin 对象的 DataTemplate

以下屏幕截图显示了一个 Map,其中显示了一个使用数据绑定的 Pin 集合:

iOS 和 Android 上带有数据绑定图钉的地图屏幕截图

在运行时选择项外观

可通过将 ItemTemplateSelector 属性设置为 DataTemplateSelector,在运行时根据项值选择 IEnumerable 集合中每个项的外观:

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

以下示例显示了 MapItemTemplateSelector 类:

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;
    }
}

MapItemTemplateSelector 类定义 DefaultTemplateXamarinTemplate DataTemplate 属性,这些属性设置为不同的数据模板。 OnSelectTemplate 方法返回 XamarinTemplate,点击 Pin 时,后者将“Xamarin”显示为标签,此时项的地址包含“San Francisco”。 如果项的地址不包含“San Francisco”,OnSelectTemplate 方法将返回 DefaultTemplate

注意

此功能的用例是基于 Pin 子类型将子类化 Pin 对象的属性绑定到其他属性。

有关数据模板选择器的详细信息,请参阅创建 Xamarin.Forms DataTemplateSelector