new GalleryPageFactory(() => new ListRefresh(), "ListView.PullToRefresh"),
new GalleryPageFactory(() => new ListViewDemoPage(), "ListView Demo Gallery - Legacy"),
new GalleryPageFactory(() => new MapGallery(), "Map Gallery - Legacy"),
+ new GalleryPageFactory(() => new MapWithItemsSourceGallery(), "Map With ItemsSource Gallery - Legacy"),
new GalleryPageFactory(() => new MinimumSizeGallery(), "MinimumSize Gallery - Legacy"),
new GalleryPageFactory(() => new MultiGallery(), "Multi Gallery - Legacy"),
new GalleryPageFactory(() => new NavigationMenuGallery(), "NavigationMenu Gallery - Legacy"),
--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:map="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Maps"
+ x:Class="Xamarin.Forms.Controls.GalleryPages.MapWithItemsSourceGallery">
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition />
+ <RowDefinition Height="Auto" />
+ </Grid.RowDefinitions>
+ <map:Map ItemsSource="{Binding Places}"
+ x:Name="_map">
+ <map:Map.ItemTemplate>
+ <DataTemplate>
+ <map:Pin Position="{Binding Position}"
+ Address="{Binding Address}"
+ Label="{Binding Description}" />
+ </DataTemplate>
+ </map:Map.ItemTemplate>
+ </map:Map>
+ <StackLayout Orientation="Horizontal"
+ Grid.Row="1">
+ <Button Text="Add"
+ Command="{Binding AddPlaceCommand}" />
+ <Button Text="Remove"
+ Command="{Binding RemovePlaceCommand}" />
+ <Button Text="Clear"
+ Command="{Binding ClearPlacesCommand}" />
+ <Button Text="Update"
+ Command="{Binding UpdatePlacesCommand}" />
+ <Button Text="Replace"
+ Command="{Binding ReplacePlaceCommand}" />
+ </StackLayout>
+ </Grid>
+</ContentPage>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using System.Windows.Input;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Maps;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Forms.Controls.GalleryPages
+{
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class MapWithItemsSourceGallery : ContentPage
+ {
+ static readonly Position startPosition = new Position(39.8283459, -98.5794797);
+
+ public MapWithItemsSourceGallery()
+ {
+ InitializeComponent();
+ BindingContext = new ViewModel();
+ _map.MoveToRegion(MapSpan.FromCenterAndRadius(startPosition, Distance.FromMiles(1200)));
+ }
+
+ [Preserve(AllMembers = true)]
+ class ViewModel
+ {
+ int _pinCreatedCount = 0;
+
+ readonly ObservableCollection<Place> _places;
+
+ public IEnumerable Places => _places;
+
+ public ICommand AddPlaceCommand { get; }
+ public ICommand RemovePlaceCommand { get; }
+ public ICommand ClearPlacesCommand { get; }
+ public ICommand UpdatePlacesCommand { get; }
+ public ICommand ReplacePlaceCommand { get; }
+
+ public ViewModel()
+ {
+ _places = new ObservableCollection<Place>() {
+ new Place("New York, USA", "The City That Never Sleeps", new Position(40.67, -73.94)),
+ new Place("Los Angeles, USA", "City of Angels", new Position(34.11, -118.41)),
+ new Place("San Francisco, USA", "Bay City ", new Position(37.77, -122.45))
+ };
+
+ AddPlaceCommand = new Command(AddPlace);
+ RemovePlaceCommand = new Command(RemovePlace);
+ ClearPlacesCommand = new Command(() => _places.Clear());
+ UpdatePlacesCommand = new Command(UpdatePlaces);
+ ReplacePlaceCommand = new Command(ReplacePlace);
+ }
+
+ void AddPlace()
+ {
+ _places.Add(NewPlace());
+ }
+
+ void RemovePlace()
+ {
+ if (_places.Any())
+ {
+ _places.Remove(_places.First());
+ }
+ }
+
+ void UpdatePlaces()
+ {
+ if (!_places.Any())
+ {
+ return;
+ }
+
+ double lastLatitude = _places.Last().Position.Latitude;
+
+ foreach (Place place in Places)
+ {
+ place.Position = new Position(lastLatitude, place.Position.Longitude);
+ }
+ }
+
+ void ReplacePlace()
+ {
+ if (!_places.Any())
+ {
+ return;
+ }
+
+ _places[_places.Count - 1] = NewPlace();
+ }
+
+ static class RandomPosition
+ {
+ static Random Random = new Random(Environment.TickCount);
+
+ public static Position Next()
+ {
+ return new Position(
+ latitude: Random.NextDouble() * 180 - 90,
+ longitude: Random.NextDouble() * 360 - 180);
+ }
+
+ public static Position Next(Position position, double latitudeRange, double longitudeRange)
+ {
+ return new Position(
+ latitude: position.Latitude + (Random.NextDouble() * 2 - 1) * latitudeRange,
+ longitude: position.Longitude + (Random.NextDouble() * 2 - 1) * longitudeRange);
+ }
+ }
+
+ Place NewPlace()
+ {
+ ++_pinCreatedCount;
+
+ return new Place(
+ $"Pin {_pinCreatedCount}",
+ $"Desc {_pinCreatedCount}",
+ RandomPosition.Next(startPosition, 8, 19));
+ }
+ }
+
+ [Preserve(AllMembers = true)]
+ class Place : INotifyPropertyChanged
+ {
+ Position _position;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ public string Address { get; }
+
+ public string Description { get; }
+
+ public Position Position
+ {
+ get => _position;
+ set
+ {
+ if (!_position.Equals(value))
+ {
+ _position = value;
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Position)));
+ }
+ }
+ }
+
+ public Place(string address, string description, Position position)
+ {
+ Address = address;
+ Description = description;
+ Position = position;
+ }
+ }
+ }
+}
\ No newline at end of file
<EmbeddedResource Include="GalleryPages\crimson.jpg" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
<PackageReference Include="Xam.Plugin.DeviceInfo" Version="3.0.2" />
+ <Compile Update="GalleryPages\BindableLayoutGalleryPage.xaml.cs">
+ <DependentUpon>BindableLayoutGalleryPage.xaml</DependentUpon>
+ </Compile>
+ <Compile Update="GalleryPages\VisualStateManagerGalleries\OnPlatformExample.xaml.cs">
+ <DependentUpon>OnPlatformExample.xaml</DependentUpon>
+ </Compile>
+ <EmbeddedResource Update="GalleryPages\BindableLayoutGalleryPage.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\MapWithItemsSourceGallery.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\TitleView.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\VisualStateManagerGalleries\ButtonDisabledStatesGallery.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\VisualStateManagerGalleries\DatePickerDisabledStatesGallery.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\VisualStateManagerGalleries\PickerDisabledStatesGallery.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\VisualStateManagerGalleries\SearchBarDisabledStatesGallery.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\VisualStateManagerGalleries\TimePickerDisabledStatesGallery.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\VisualStateManagerGalleries\ValidationExample.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\XamlPage.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="ControlGalleryPages\BehaviorsAndTriggers.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\StyleXamlGallery.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\XamlNativeViews.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\MacTwitterDemo.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="HanselForms\MyAbout.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="HanselForms\BlogPage.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="HanselForms\TwitterPage.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="CompressedLayout.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="ControlGalleryPages\LayoutAddPerformance.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
+ <EmbeddedResource Update="GalleryPages\ControlTemplateXamlPage.xaml">
+ <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
+ </EmbeddedResource>
</ItemGroup>
<Target Name="CreateControllGalleryConfig" BeforeTargets="Build">
<CreateItem Include="blank.config">
-using System;
using NUnit.Framework;
+using System;
+using System.Collections;
+using System.Collections.ObjectModel;
+using System.Linq;
using Xamarin.Forms.Maps;
namespace Xamarin.Forms.Core.UnitTests
Assert.False (signaled);
}
+
+ [Test]
+ public void TracksEmpty()
+ {
+ var map = new Map();
+
+ var itemsSource = new ObservableCollection<int>();
+ map.ItemsSource = itemsSource;
+ map.ItemTemplate = new DataTemplate();
+
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ }
+
+ [Test]
+ public void TracksAdd()
+ {
+ var itemsSource = new ObservableCollection<int>();
+
+ var map = new Map()
+ {
+ ItemsSource = itemsSource,
+ ItemTemplate = GetItemTemplate()
+ };
+
+ itemsSource.Add(1);
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ }
+
+ [Test]
+ public void TracksInsert()
+ {
+ var itemsSource = new ObservableCollection<int>();
+
+ var map = new Map()
+ {
+ ItemsSource = itemsSource,
+ ItemTemplate = GetItemTemplate()
+ };
+
+ itemsSource.Insert(0, 1);
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ }
+
+ [Test]
+ public void TracksRemove()
+ {
+ var itemsSource = new ObservableCollection<int>() { 0, 1 };
+
+ var map = new Map()
+ {
+ ItemsSource = itemsSource,
+ ItemTemplate = GetItemTemplate()
+ };
+
+ itemsSource.RemoveAt(0);
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+
+ itemsSource.Remove(1);
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ }
+
+ [Test]
+ public void TracksReplace()
+ {
+ var itemsSource = new ObservableCollection<int>() { 0, 1, 2 };
+
+ var map = new Map()
+ {
+ ItemsSource = itemsSource,
+ ItemTemplate = GetItemTemplate()
+ };
+
+ itemsSource[0] = 3;
+ itemsSource[1] = 4;
+ itemsSource[2] = 5;
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ }
+
+ [Test]
+ public void ItemMove()
+ {
+ var itemsSource = new ObservableCollection<int>() { 0, 1 };
+
+ var map = new Map()
+ {
+ ItemsSource = itemsSource,
+ ItemTemplate = GetItemTemplate()
+ };
+
+ itemsSource.Move(0, 1);
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+
+ itemsSource.Move(1, 0);
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ }
+
+ [Test]
+ public void TracksClear()
+ {
+ var itemsSource = new ObservableCollection<int>() { 0, 1 };
+
+ var map = new Map()
+ {
+ ItemsSource = itemsSource,
+ ItemTemplate = GetItemTemplate()
+ };
+
+ itemsSource.Clear();
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ }
+
+ [Test]
+ public void TracksNull()
+ {
+ var map = new Map()
+ {
+ ItemTemplate = GetItemTemplate()
+ };
+
+ var itemsSource = new ObservableCollection<int>(Enumerable.Range(0, 10));
+ map.ItemsSource = itemsSource;
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+
+ itemsSource = null;
+ map.ItemsSource = itemsSource;
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ }
+
+ [Test]
+ public void TracksItemTemplate()
+ {
+ var map = new Map()
+ {
+ ItemTemplate = GetItemTemplate()
+ };
+
+ var itemsSource = new ObservableCollection<int>(Enumerable.Range(0, 3));
+ map.ItemsSource = itemsSource;
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ foreach (Pin pin in map.Pins)
+ {
+ Assert.IsTrue(pin.Address == "Address");
+ }
+
+ map.ItemTemplate = GetItemTemplate("Address 2");
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ foreach(Pin pin in map.Pins)
+ {
+ Assert.IsTrue(pin.Address == "Address 2");
+ }
+ }
+
+ [Test]
+ public void ItemsSourceTakePrecendenceOverPins()
+ {
+ var map = new Map()
+ {
+ ItemTemplate = GetItemTemplate()
+ };
+
+ map.Pins.Add(new Pin() { Label = "Label" });
+ map.Pins.Add(new Pin() { Label = "Label" });
+
+ var itemsSource = new ObservableCollection<int>(Enumerable.Range(0, 10));
+ map.ItemsSource = itemsSource;
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ }
+
+ [Test]
+ public void ElementIsGarbageCollectedAfterItsRemoved()
+ {
+ var map = new Map()
+ {
+ ItemTemplate = GetItemTemplate()
+ };
+
+ // Create a view-model and bind the map to it
+ map.SetBinding(Map.ItemsSourceProperty, new Binding(nameof(MockViewModel.Items)));
+ map.BindingContext = new MockViewModel(new ObservableCollection<int>(Enumerable.Range(0, 10)));
+
+ // Set ItemsSource
+ var itemsSource = new ObservableCollection<int>(Enumerable.Range(0, 10));
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ itemsSource = null;
+
+ // Remove map from container
+ var pageRoot = new Grid();
+ pageRoot.Children.Add(map);
+ var page = new ContentPage() { Content = pageRoot };
+
+ var weakReference = new WeakReference(map);
+ pageRoot.Children.Remove(map);
+ map = null;
+
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+
+ Assert.IsFalse(weakReference.IsAlive);
+ }
+
+ [Test]
+ public void ThrowsExceptionOnUsingDataTemplateSelectorForItemTemplate()
+ {
+ var map = new Map();
+
+ var itemsSource = new ObservableCollection<int>(Enumerable.Range(0, 10));
+ map.ItemsSource = itemsSource;
+
+ Assert.Throws(typeof(NotSupportedException), () => map.ItemTemplate = GetDataTemplateSelector());
+ }
+
+ [Test]
+ public void DontTrackAfterItemsSourceChanged()
+ {
+ var map = new Map()
+ {
+ ItemTemplate = GetItemTemplate()
+ };
+
+ var itemsSource = new ObservableCollection<int>(Enumerable.Range(0, 10));
+ map.ItemsSource = itemsSource;
+ map.ItemsSource = new ObservableCollection<int>(Enumerable.Range(0, 10));
+
+ itemsSource.Add(11);
+ Assert.IsTrue(itemsSource.Count() == 11);
+ }
+
+ [Test]
+ public void WorksWithNullItems()
+ {
+ var map = new Map()
+ {
+ ItemTemplate = GetItemTemplate()
+ };
+
+ var itemsSource = new ObservableCollection<int?>(Enumerable.Range(0, 10).Cast<int?>());
+ itemsSource.Add(null);
+ map.ItemsSource = itemsSource;
+ Assert.IsTrue(IsMapWithItemsSource(itemsSource, map));
+ }
+
+ // Checks if for every item in the items source there's a corresponding pin
+ static bool IsMapWithItemsSource(IEnumerable itemsSource, Map map)
+ {
+ if (itemsSource == null)
+ {
+ return true;
+ }
+
+ if (map.ItemTemplate == null)
+ {
+ // If ItemsSource is set but ItemTemplate is not, there should not be any Pins
+ return !map.Pins.Any();
+ }
+
+ int i = 0;
+ foreach (object item in itemsSource)
+ {
+ // Pins collection order are not tracked, so just make sure a Pin for item exists
+ if (!map.Pins.Any(p => Equals(item, p.BindingContext)))
+ {
+ return false;
+ }
+
+ ++i;
+ }
+
+ return map.Pins.Count == i;
+ }
+
+ static DataTemplate GetItemTemplate(string address = null)
+ {
+ return new DataTemplate(() => new Pin()
+ {
+ Address = address ?? "Address",
+ Label = "Label",
+ Position = new Position()
+ });
+ }
+
+ static DataTemplateSelector GetDataTemplateSelector()
+ {
+ return new TestDataTemplateSelector();
+ }
+
+ class TestDataTemplateSelector : DataTemplateSelector
+ {
+ readonly DataTemplate dt = GetItemTemplate();
+
+ protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
+ {
+ return dt;
+ }
+ }
+
+ class MockViewModel
+ {
+ public IEnumerable Items { get; }
+ public MockViewModel(IEnumerable itemsSource)
+ {
+ Items = itemsSource;
+ }
+ }
}
}
public static readonly BindableProperty HasZoomEnabledProperty = BindableProperty.Create("HasZoomEnabled", typeof(bool), typeof(Map), true);
+ public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(IEnumerable), typeof(IEnumerable), typeof(Map), default(IEnumerable),
+ propertyChanged: (b, o, n) => ((Map)b).OnItemsSourcePropertyChanged((IEnumerable)o, (IEnumerable)n));
+
+ public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(Map), default(DataTemplate),
+ propertyChanged: (b, o, n) => ((Map)b).OnItemTemplatePropertyChanged((DataTemplate)o, (DataTemplate)n));
+
readonly ObservableCollection<Pin> _pins = new ObservableCollection<Pin>();
MapSpan _visibleRegion;
get { return _pins; }
}
+ public IEnumerable ItemsSource
+ {
+ get { return (IEnumerable)GetValue(ItemsSourceProperty); }
+ set { SetValue(ItemsSourceProperty, value); }
+ }
+
+ public DataTemplate ItemTemplate
+ {
+ get { return (DataTemplate)GetValue(ItemTemplateProperty); }
+ set { SetValue(ItemTemplateProperty, value); }
+ }
+
[EditorBrowsable(EditorBrowsableState.Never)]
public void SetVisibleRegion(MapSpan value) => VisibleRegion = value;
public MapSpan VisibleRegion
if (e.NewItems != null && e.NewItems.Cast<Pin>().Any(pin => pin.Label == null))
throw new ArgumentException("Pin must have a Label to be added to a map");
}
+
+ void OnItemsSourcePropertyChanged(IEnumerable oldItemsSource, IEnumerable newItemsSource)
+ {
+ if (oldItemsSource is INotifyCollectionChanged ncc)
+ {
+ ncc.CollectionChanged -= OnItemsSourceCollectionChanged;
+ }
+
+ if (newItemsSource is INotifyCollectionChanged ncc1)
+ {
+ ncc1.CollectionChanged += OnItemsSourceCollectionChanged;
+ }
+
+ _pins.Clear();
+ CreatePinItems();
+ }
+
+ void OnItemTemplatePropertyChanged(DataTemplate oldItemTemplate, DataTemplate newItemTemplate)
+ {
+ if (newItemTemplate is DataTemplateSelector)
+ {
+ throw new NotSupportedException($"You are using an instance of {nameof(DataTemplateSelector)} to set the {nameof(Map)}.{ItemTemplateProperty.PropertyName} property. Use an instance of a {nameof(DataTemplate)} property instead to set an item template.");
+ }
+
+ _pins.Clear();
+ CreatePinItems();
+ }
+
+ void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ switch (e.Action)
+ {
+ case NotifyCollectionChangedAction.Add:
+ if (e.NewStartingIndex == -1)
+ goto case NotifyCollectionChangedAction.Reset;
+ foreach (object item in e.NewItems)
+ CreatePin(item);
+ break;
+ case NotifyCollectionChangedAction.Move:
+ if (e.OldStartingIndex == -1 || e.NewStartingIndex == -1)
+ goto case NotifyCollectionChangedAction.Reset;
+ // Not tracking order
+ break;
+ case NotifyCollectionChangedAction.Remove:
+ if (e.OldStartingIndex == -1)
+ goto case NotifyCollectionChangedAction.Reset;
+ foreach (object item in e.OldItems)
+ RemovePin(item);
+ break;
+ case NotifyCollectionChangedAction.Replace:
+ if (e.OldStartingIndex == -1)
+ goto case NotifyCollectionChangedAction.Reset;
+ foreach (object item in e.OldItems)
+ RemovePin(item);
+ foreach (object item in e.NewItems)
+ CreatePin(item);
+ break;
+ case NotifyCollectionChangedAction.Reset:
+ _pins.Clear();
+ break;
+ }
+ }
+
+ void CreatePinItems()
+ {
+ if (ItemsSource == null || ItemTemplate == null)
+ {
+ return;
+ }
+
+ foreach (object item in ItemsSource)
+ {
+ CreatePin(item);
+ }
+ }
+
+ void CreatePin(object newItem)
+ {
+ if (ItemTemplate == null)
+ {
+ return;
+ }
+
+ var pin = (Pin)ItemTemplate.CreateContent();
+ pin.BindingContext = newItem;
+ _pins.Add(pin);
+ }
+
+ void RemovePin(object itemToRemove)
+ {
+ Pin pinToRemove = _pins.FirstOrDefault(pin => pin.BindingContext?.Equals(itemToRemove) == true);
+ if (pinToRemove != null)
+ {
+ _pins.Remove(pinToRemove);
+ }
+ }
}
}
\ No newline at end of file
namespace Xamarin.Forms.Maps
{
- public class Pin : BindableObject
+ public class Pin : Element
{
public static readonly BindableProperty TypeProperty = BindableProperty.Create("Type", typeof(PinType), typeof(Pin), default(PinType));