// an abstract helper class public abstract class MvxAnnotationManager { private readonly MKMapView _mapView; private IEnumerable _itemsSource; private IDisposable _subscription; public Dictionary<object, MKAnnotation> _annotations = new Dictionary<object, MKAnnotation>(); protected MvxAnnotationManager(MKMapView mapView) { _mapView = mapView; } // MvxSetToNullAfterBinding isn't strictly needed any more // - but it's nice to have for when binding is torn down [MvxSetToNullAfterBinding] public virtual IEnumerable ItemsSource { get { return _itemsSource; } set { SetItemsSource(value); } } protected virtual void SetItemsSource(IEnumerable value) { if (_itemsSource == value) return; if (_subscription != null) { _subscription.Dispose(); _subscription = null; } _itemsSource = value; if (_itemsSource != null && !(_itemsSource is IList)) MvxBindingTrace.Trace(MvxTraceLevel.Warning, "Binding to IEnumerable rather than IList - this can be inefficient, especially for large lists"); ReloadAllAnnotations(); var newObservable = _itemsSource as INotifyCollectionChanged; if (newObservable != null) { _subscription = newObservable.WeakSubscribe(OnItemsSourceCollectionChanged); } } protected virtual void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: AddAnnotations(e.NewItems); break; case NotifyCollectionChangedAction.Remove: RemoveAnnotations(e.OldItems); break; case NotifyCollectionChangedAction.Replace: RemoveAnnotations(e.OldItems); AddAnnotations(e.NewItems); break; case NotifyCollectionChangedAction.Move: // not interested in this break; case NotifyCollectionChangedAction.Reset: ReloadAllAnnotations(); break; default: throw new ArgumentOutOfRangeException(); } } protected virtual void ReloadAllAnnotations() { _mapView.RemoveAnnotations(_annotations.Values.Select(x => (NSObject)x).ToArray()); _annotations.Clear(); if (_itemsSource == null) return; AddAnnotations(_itemsSource); } protected abstract MKAnnotation CreateAnnotation(object item); protected virtual void RemoveAnnotations(IEnumerable oldItems) { foreach (var item in oldItems) { RemoveAnnotationFor(item); } } protected virtual void RemoveAnnotationFor(object item) { var annotation = _annotations[item]; _mapView.RemoveAnnotation(annotation); _annotations.Remove(item); } protected virtual void AddAnnotations(IEnumerable newItems) { foreach (object item in newItems) { AddAnnotationFor(item); } } protected virtual void AddAnnotationFor(object item) { var annotation = CreateAnnotation(item); _annotations[item] = annotation; _mapView.AddAnnotation(annotation); } } // example use public class House { public double Lat { get; set; } public double Lng { get; set; } public string Name { get; set; } } public class FirstViewModel : MvxViewModel { public ObservableCollection<House> Houses { get; set; } } public class HouseAnnotationManager : MvxAnnotationManager { public HouseAnnotationManager(MKMapView mapView) : base(mapView) { } protected override MKAnnotation CreateAnnotation(object item) { var house = item as House; return new HouseAnnotation(item); } } public class HouseAnnotation : MKAnnotation { public HouseAnnotation(House house) { // use house here... // in theory you could also data-bind to the house too (e.g. if it's location were to move...) } public override CLLocationCoordinate2D Coordinate { get; set; } } [Register("FirstView")] public class FirstView : MvxViewController { private HouseAnnotationManager _manager; public override void ViewDidLoad() { View = new UIView(){ BackgroundColor = UIColor.White}; base.ViewDidLoad(); var myMapView = new MKMapView(View.Frame); Add(myMapView); myMapView.Delegate = new MyMapDelegate(); // standard map delegate - must provide the annotation views _manager = new HouseAnnotationManager(myMapView); var set = this.CreateBindingSet<FirstView, FirstViewModel>(); set.Bind(_manager).For(m => m.ItemsSource).To(vm => vm.HouseList); set.Apply(); } }