Project structure,
  • PCL (With Xaml Views)
  • .Android project (Targeting android platform)
  • .iOS (Targeting iOS platform)
The objective of this project is to use Xamarin.Forms MAP on different platforms with the help of renderer (native code).
  1. Create a custom Map Control in PCL.
  2. Now, there is VisibleRegion property for Xamarin.Forms map and we can detect visible region changed in Xaml.CS.
  3. But, as soon as I added renderer, I got the visible region null. I have added one function in the custom map for visible region changed, in order to get a call back from the renderer.
  4. On Marker and pin tap, there is no event available at XAML level because we are writing the renderer.
In order to overcome this, we have to add one function in CustomMap (PinClicked).
  1. public class CustomMap : Map
  2. {
  3. //This bindable property will provide Track information for map
  4. //Will be accessible from Xaml view for binding
  5. public static readonly BindableProperty RouteCoordinatesProperty =
  6. BindableProperty.Create("RouteCoordinates", typeof(List<TrackInfo>), typeof(CustomMap), null, BindingMode.TwoWay);
  7. public List<TrackInfo> RouteCoordinates
  8. {
  9. get { return (List<TrackInfo>)GetValue(RouteCoordinatesProperty); }
  10. set { SetValue(RouteCoordinatesProperty, value); }
  11. }
  12. //This bindable property will contain collection of pins
  13. //CustomPin Model will have Pin Metadata
  14. public static readonly BindableProperty CustomPinsProperty =
  15. BindableProperty.Create("CustomPins", typeof(List<CustomPin>), typeof(CustomMap), null, BindingMode.TwoWay);
  16. public List<CustomPin> CustomPins
  17. {
  18. get { return (List<CustomPin>)GetValue(CustomPinsProperty); }
  19. set
  20. {
  21. SetValue(CustomPinsProperty, value);
  22. }
  23. }
  24. public Action<SignDetails> PinClicked;
  25. public Func<string,string,Task> OnVisibleRegionChanged;
  26. public CustomMap()
  27. {
  28. }
  29. public async Task<GeoPosition> GetGeolocation()
  30. {
  31. var result = await App.Container.GetInstance<IGeolocationService>().GetCurrentLocation();
  32. return result;
  33. }
  34. }
This CustomMap in PCL contains several binding properties in order to bind Pins and Tracks to the map from XAML.
CustomMap class uses CustomPin (pin details) and TrackInfo Model,
  1. public class CustomPin
  2. {
  3. //This Xamarin forms Pin Class
  4. //This pin Class contains several
  5. //Properties like Position.Latitude
  6. //Position.Longitude etc
  7. public Pin pin { get; set; }
  8. public string pinImage { get; set; }
  9. public string PinPosition { get; set; }
  10. }
TrackInfo
  1. public class GeoPosition
  2. {
  3. public double Latitude { get; set; }
  4. public double Longitude { get; set; }
  5. }
  6. public class PositionEstimateList
  7. {
  8. public PositionEstimateList()
  9. {
  10. actualPosition = new List<GeoPosition>();
  11. }
  12. public List<GeoPosition> actualPosition { get; set; }
  13. }
  14. public class TrackInfo
  15. {
  16. public string SubmitterName { get; set; }
  17. public string trackColor { get; set; }
  18. public List<PositionEstimateList> PositionEstimateList { get; set; }
  19. }
Here is the XAML snippet from Home.Xaml (Where I am using my CustomMap),
  1. <local:CustomMap RouteCoordinates="{Binding RouteCoordinates}"
  2. CustomPins="{Binding CustomPins}"
  3. VerticalOptions="FillAndExpand"
  4. x:Name="TrackingMap"/>
Android renderer
  1. public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback, IOnMarkerClickListener
  2. {
  3. public string TopLeft { get; set; }
  4. public string BottomRight { get; set; }
  5. List<Marker> markers;
  6. GoogleMap map;
  7. Polyline polyline;
  8. public double CacheLat { get; set; }
  9. public double CacheLong { get; set; }
  10. List<Polyline> PolylineBuffer = new List<Polyline>();
  11. bool isDrawn;
  12. public static Random rand = new Random();
  13. public static Android.Graphics.Color GetRandomColor()
  14. {
  15. int hue = rand.Next(255);
  16. Android.Graphics.Color color = Android.Graphics.Color.HSVToColor(
  17. new[] {
  18. hue,
  19. 1.0f,
  20. 1.0f,
  21. }
  22. );
  23. return color;
  24. }
  25. protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
  26. {
  27. base.OnElementChanged(e);
  28. if (e.OldElement != null)
  29. {
  30. // Unsubscribe
  31. }
  32. if (e.NewElement != null)
  33. {
  34. ((MapView)Control).GetMapAsync(this);
  35. }
  36. }
  37. /// <summary>
  38. /// this event notifies camerachange to viewModel by using OnVisibleRegionChanged delegate
  39. /// </summary>
  40. /// <param name="sender"></param>
  41. /// <param name="e"></param>
  42. private async void Map_CameraChange(object sender, CameraChangeEventArgs e)
  43. {
  44. map = sender as GoogleMap;
  45. var zoom = e.Position.Zoom;
  46. if (((CustomMap)this.Element) != null && map != null)
  47. {
  48. var projection = map.Projection.VisibleRegion;
  49. var far_right_lat = Math.Round(projection.FarLeft.Latitude, 2).ToString();
  50. var far_right_long = Math.Round(projection.FarLeft.Longitude, 2).ToString();
  51. var near_left_lat = Math.Round(projection.NearRight.Latitude, 2).ToString();
  52. var near_left_long = Math.Round(projection.NearRight.Longitude, 2).ToString();
  53. var centerLatLong = projection.LatLngBounds.Center;
  54. if (centerLatLong != null)
  55. {
  56. App.CurrentLat = centerLatLong.Latitude;
  57. App.CurrentLong = centerLatLong.Longitude;
  58. App.CurrentZoomLevel = zoom;
  59. }
  60. var near_left = near_left_lat + "," + near_left_long;
  61. var near_Right = far_right_lat + "," + far_right_long;
  62. await ((CustomMap)this.Element).OnVisibleRegionChanged(near_left, near_Right);
  63. }
  64. }
  65. /// <summary>
  66. /// This method will detect which colllection is changed at runtime
  67. /// and will help to redraw map accordingly.
  68. /// RouteCoordinatesProperty is used to draw tracks as and when information of track changes from HomeViewModel.
  69. /// CustomPin property is used to redraw pins as and when collection of custom pin changes from HomeViewModel.
  70. /// </summary>
  71. /// <param name="sender"></param>
  72. /// <param name="e"></param>
  73. protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
  74. {
  75. base.OnElementPropertyChanged(sender, e);
  76. if (this.Element == null || this.Control == null)
  77. return;
  78. if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
  79. {
  80. var nativeMap = Control as MapView;
  81. if (((CustomMap)this.Element).RouteCoordinates == null)
  82. {
  83. RemovePlolyline();
  84. }
  85. else
  86. if (((CustomMap)this.Element).RouteCoordinates.Count == 0)
  87. {
  88. RemovePlolyline();
  89. }
  90. else
  91. {
  92. UpdatePolyLine();
  93. }
  94. }
  95. if (e.PropertyName == CustomMap.CustomPinsProperty.PropertyName)
  96. {
  97. var nativeMap = Control as MapView;
  98. if (markers != null)
  99. {
  100. if (markers.Count > 0)
  101. markers.ForEach(x => x.Remove());
  102. }
  103. SetMapMarkers();
  104. }
  105. }
  106. private void SetMapMarkers()
  107. {
  108. markers = new List<Marker>();
  109. if (((CustomMap)this.Element).CustomPins == null)
  110. return;
  111. foreach (var custompin in ((CustomMap)this.Element).CustomPins)
  112. {
  113. var marker = new MarkerOptions();
  114. marker.SetPosition(new LatLng(custompin.pin.Position.Latitude, custompin.pin.Position.Longitude));
  115. marker.SetTitle(custompin.pin.Label);
  116. marker.SetSnippet(custompin.pin.Address);
  117. var resource = typeof(Resource.Drawable).GetField(custompin.pinImage);
  118. int resourceId = 0;
  119. if (resource != null)
  120. {
  121. resourceId = (int)resource.GetValue(custompin.pinImage);
  122. }
  123. if (resourceId != 0)
  124. {
  125. marker.SetIcon(BitmapDescriptorFactory.FromResource(resourceId));
  126. map.SetOnMarkerClickListener(this);
  127. Marker m = map.AddMarker(marker);
  128. markers.Add(m);
  129. }
  130. }
  131. isDrawn = true;
  132. }
  133. /// <summary>
  134. /// this is OnMarker click event it occurs when pin is tapped
  135. /// There is PinClicked Action which is implemented in ViewModel.
  136. /// this provision allows us to do whatever we want from viewModel after the event
  137. /// Occurs.
  138. /// </summary>
  139. /// <param name="marker"></param>
  140. /// <returns></returns>
  141. public bool OnMarkerClick(Marker marker)
  142. {
  143. SignDetails _markerDetails = new SignDetails();
  144. foreach (var pin in ((CustomMap)this.Element).CustomPins)
  145. {
  146. if (pin.pin.Position.Latitude == marker.Position.Latitude)
  147. {
  148. if (pin.pin.Position.Longitude == marker.Position.Longitude)
  149. {
  150. if (!string.IsNullOrEmpty(pin.pin.Label))
  151. {
  152. if (pin.pin.Label == marker.Title)
  153. {
  154. _markerDetails.SignLat = pin.pin.Position.Latitude.ToString();
  155. _markerDetails.SignLong = pin.pin.Position.Longitude.ToString();
  156. _markerDetails.SignImage = pin.pinImage;
  157. }
  158. }
  159. }
  160. }
  161. }
  162. ((CustomMap)this.Element).PinClicked(_markerDetails);
  163. return true;
  164. }
  165. protected override void OnLayout(bool changed, int l, int t, int r, int b)
  166. {
  167. base.OnLayout(changed, l, t, r, b);
  168. if (changed)
  169. {
  170. isDrawn = false;
  171. }
  172. }
  173. /// <summary>
  174. /// this method will draw polyline on Map
  175. /// </summary>
  176. private void UpdatePolyLine()
  177. {
  178. try
  179. {
  180. RemovePlolyline();
  181. foreach (var routeCordinates in ((CustomMap)this.Element).RouteCoordinates)
  182. {
  183. foreach (var positionEstimate in routeCordinates.PositionEstimateList)
  184. {
  185. if (routeCordinates.SubmitterName == null)
  186. return;
  187. var color = routeCordinates.trackColor;
  188. if (!string.IsNullOrEmpty(color))
  189. {
  190. var trackColor = GetColor(color);
  191. var polylineOptions = new PolylineOptions();
  192. polylineOptions.InvokeColor(trackColor);
  193. foreach (var position in positionEstimate.actualPosition)
  194. {
  195. polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
  196. }
  197. polyline = map.AddPolyline(polylineOptions);
  198. PolylineBuffer.Add(polyline);
  199. }
  200. }
  201. }
  202. }
  203. catch (Exception ex)
  204. {
  205. //Log exception to hockey app
  206. }
  207. }
  208. /// <summary>
  209. /// here it is restricted to show the 5 tracks only so 5 colors are shown for 5 tracks
  210. /// one can easily modify by using random color and showing legends accordingly for tracks.
  211. /// </summary>
  212. /// <param name="color"></param>
  213. /// <returns></returns>
  214. private Android.Graphics.Color GetColor(string color)
  215. {
  216. Android.Graphics.Color trackColor = Android.Graphics.Color.Orange;
  217. switch (color)
  218. {
  219. case "1":
  220. trackColor = Android.Graphics.Color.Rgb(77, 123, 224);
  221. break;
  222. case "2":
  223. trackColor = Android.Graphics.Color.Rgb(50, 193, 214);
  224. break;
  225. case "3":
  226. trackColor = Android.Graphics.Color.Rgb(163, 178, 78);
  227. break;
  228. case "4":
  229. trackColor = Android.Graphics.Color.Rgb(187, 93, 153);
  230. break;
  231. case "5":
  232. trackColor = Android.Graphics.Color.Rgb(175, 98, 46);
  233. break;
  234. }
  235. return trackColor;
  236. }
  237. /// <summary>
  238. /// Removing multiple polylines at once
  239. /// </summary>
  240. private void RemovePlolyline()
  241. {
  242. foreach (Polyline line in PolylineBuffer)
  243. {
  244. line.Remove();
  245. }
  246. PolylineBuffer.Clear();
  247. }
  248. bool _isReady;
  249. public async void OnMapReady(GoogleMap googleMap)
  250. {
  251. try
  252. {
  253. if (_isReady) return;
  254. _isReady = true;
  255. map = googleMap;
  256. var bounds = googleMap.Projection.VisibleRegion;
  257. if (App.CurrentLat == 0 && App.CurrentLong == 0)
  258. {
  259. var CurrentPosition = await ((CustomMap)this.Element).GetGeolocation();
  260. if (CurrentPosition != null)
  261. {
  262. App.CurrentLat = CurrentPosition.Latitude;
  263. App.CurrentLong = CurrentPosition.Longitude;
  264. map.MoveCamera(CameraUpdateFactory.NewLatLngZoom(new LatLng(App.CurrentLat, App.CurrentLong),
  265. App.CurrentZoomLevel));
  266. }
  267. else
  268. {
  269. map.MoveCamera(CameraUpdateFactory.NewLatLngZoom(new LatLng(52.52, 13.40), App.CurrentZoomLevel));
  270. }
  271. }
  272. else
  273. {
  274. map.MoveCamera(CameraUpdateFactory.NewLatLngZoom(new LatLng(App.CurrentLat, App.CurrentLong), App.CurrentZoomLevel));
  275. }
  276. map.CameraChange += Map_CameraChange;
  277. await Task.Run(() =>
  278. {
  279. if (((CustomMap)this.Element).RouteCoordinates != null)
  280. {
  281. if (((CustomMap)this.Element).RouteCoordinates.Count > 0)
  282. {
  283. UpdatePolyLine();
  284. }
  285. }
  286. });
  287. await Task.Run(() =>
  288. {
  289. if (((CustomMap)this.Element).CustomPins != null)
  290. {
  291. if (((CustomMap)this.Element).CustomPins.Count > 0)
  292. {
  293. SetMapMarkers();
  294. }
  295. }
  296. });
  297. }
  298. catch (Exception ex)
  299. {
  300. //Log exception to hockey app
  301. }
  302. }
  303. }
iOS Renderer [I got the wiered issue that mkMapview automatically resets its location to initial position if we move anywhere in map].
  1. public class CustomMapRenderer : MapRenderer
  2. {
  3. List<string> tempImages = new List<string>();
  4. public string TopLeft { get; set; }
  5. public string BottomRight { get; set; }
  6. public bool IsRegionChange = true;
  7. List<MKPolyline> _mkPolyLineList = new List<MKPolyline>();
  8. MKPolylineRenderer polylineRenderer;
  9. MKPolyline routeOverlay;
  10. CustomMap map;
  11. MKMapView nativeMap;
  12. public CustomMapRenderer()
  13. {
  14. }
  15. protected override async void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
  16. {
  17. base.OnElementPropertyChanged(sender, e);
  18. if (nativeMap == null)
  19. {
  20. nativeMap = Control as MKMapView;
  21. nativeMap.ZoomEnabled = true;
  22. nativeMap.ScrollEnabled = true;
  23. map = (CustomMap)sender;
  24. }
  25. if (nativeMap != null)
  26. if (IsRegionChange)
  27. {
  28. await InitializeMap(sender);
  29. ReDraw();
  30. IsRegionChange = false;
  31. }
  32. if ((this.Element == null) || (this.Control == null))
  33. return;
  34. if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
  35. {
  36. map = (CustomMap)sender;
  37. if (nativeMap == null)
  38. return;
  39. if (map.RouteCoordinates.Count > 0)
  40. UpdatePolyLine();
  41. else
  42. RemovePolyline();
  43. }
  44. if (e.PropertyName == CustomMap.CustomPinsProperty.PropertyName)
  45. {
  46. map = (CustomMap)sender;
  47. map.Pins.Clear();
  48. if (map.CustomPins != null)
  49. {
  50. foreach (var customPin in map.CustomPins)
  51. {
  52. map.Pins.Add(customPin.pin);
  53. }
  54. }
  55. InvokePlotPins();
  56. }
  57. }
  58. private void RemovePolyline()
  59. {
  60. try
  61. {
  62. if (_mkPolyLineList != null)
  63. {
  64. if (_mkPolyLineList.Count > 0)
  65. {
  66. var overlays = nativeMap.Overlays;
  67. nativeMap.RemoveOverlays(overlays);
  68. _mkPolyLineList.Clear();
  69. }
  70. }
  71. }
  72. catch (Exception ex)
  73. {
  74. //Log Exception to hockey app
  75. }
  76. }
  77. private void ReDraw()
  78. {
  79. if (map == null)
  80. return;
  81. if (map.RouteCoordinates != null)
  82. if (map.RouteCoordinates.Count > 0)
  83. UpdatePolyLine();
  84. if (map.CustomPins != null)
  85. if (map.CustomPins.Count > 0)
  86. InvokePlotPins();
  87. }
  88. private async System.Threading.Tasks.Task InitializeMap(object sender)
  89. {
  90. try
  91. {
  92. map = (CustomMap)sender;
  93. nativeMap.RegionChanged += NativeMap_RegionChanged;
  94. if (App.CurrentLat != 0 && App.CurrentLong != 0)
  95. map.MoveToRegion(new MapSpan(new Position(App.CurrentLat, App.CurrentLong), App.LatitudeDelta, App.LongitudeDelta));
  96. else
  97. {
  98. var location = await map.GetGeolocation();
  99. if (location != null)
  100. {
  101. map.MoveToRegion(new MapSpan(new Position(location.Latitude,
  102. location.Longitude), 0.01, 0.01)); // where the map should start
  103. }
  104. else
  105. {
  106. map.MoveToRegion(new MapSpan(new Position
  107. (52.5200, 13.4050), 0.01, 0.01)); // where the map should start
  108. }
  109. }
  110. }
  111. catch (Exception ex)
  112. {
  113. //Log Exception to hockey app
  114. }
  115. }
  116. private async void NativeMap_RegionChanged(object sender, MKMapViewChangeEventArgs e)
  117. {
  118. try
  119. {
  120. if (Element == null)
  121. return;
  122. var position = nativeMap.VisibleMapRect;
  123. CalculateBoundingCoordinates(nativeMap);
  124. nativeMap.ZoomEnabled = true;
  125. nativeMap.ScrollEnabled = true;
  126. nativeMap.RegionChanged -= NativeMap_RegionChanged;
  127. //This solves the problem of MKMAPVIEW to Reset its view to FirstLocation
  128. map.MoveToRegion(new MapSpan(new Position(nativeMap.Region.Center.Latitude, nativeMap.Region.Center.Longitude), nativeMap.Region.Span.LatitudeDelta, nativeMap.Region.Span.LongitudeDelta));
  129. nativeMap.RegionChanged += NativeMap_RegionChanged;
  130. if (BottomRight != null && TopLeft != null)
  131. {
  132. App.CurrentLat = nativeMap.Region.Center.Latitude;
  133. App.CurrentLong = nativeMap.Region.Center.Longitude;
  134. App.LatitudeDelta = nativeMap.Region.Span.LatitudeDelta;
  135. App.LongitudeDelta = nativeMap.Region.Span.LongitudeDelta;
  136. await map.OnVisibleRegionChanged(BottomRight, TopLeft);
  137. }
  138. }
  139. catch (Exception ex)
  140. {
  141. //log Exception to hockey app
  142. }
  143. }
  144. /// <summary>
  145. /// Calulates Top-Left and Right-Bottom of Map
  146. /// </summary>
  147. /// <param name="region"></param>
  148. /// <returns></returns>
  149. public void CalculateBoundingCoordinates(MKMapView map)
  150. {
  151. var center = map.Region.Center;
  152. var halfheightDegrees = map.Region.Span.LatitudeDelta / 2;
  153. var halfwidthDegrees = map.Region.Span.LongitudeDelta / 2;
  154. var left = Math.Round(center.Longitude - halfwidthDegrees, 2);
  155. var right = Math.Round(center.Longitude + halfwidthDegrees, 2);
  156. var top = Math.Round(center.Latitude + halfheightDegrees, 2);
  157. var bottom = Math.Round(center.Latitude - halfheightDegrees, 2);
  158. if (left < -180) left = 180 + (180 + left);
  159. if (right > 180) right = (right - 180) - 180;
  160. TopLeft = top.ToString() + "," + left.ToString();
  161. BottomRight = bottom.ToString() + "," + right.ToString();
  162. }
  163. private void InvokePlotPins()
  164. {
  165. if (nativeMap == null)
  166. return;
  167. nativeMap.GetViewForAnnotation = GetViewForAnnotation;
  168. }
  169. private MKAnnotationView GetViewForAnnotation(MKMapView mapView, IMKAnnotation annotation)
  170. {
  171. MKAnnotationView annotationView = null;
  172. if (annotation is MKUserLocation)
  173. return null;
  174. var mkAnnotation = annotation as MKPointAnnotation;
  175. if (mkAnnotation == null)
  176. return null;
  177. var customPin = GetCustomPin(mkAnnotation);
  178. if (customPin == null)
  179. {
  180. return null;
  181. }
  182. SignDetails _signDetails = new SignDetails();
  183. _signDetails.SignLat = customPin.pin.Position.Latitude.ToString();
  184. _signDetails.SignLong = customPin.pin.Position.Longitude.ToString();
  185. _signDetails.SignImage = customPin.pinImage;
  186. annotationView = new CustomMKAnnotationView(annotation, customPin.pinImage)
  187. {
  188. CurrentSignDetails = new SignDetails(_signDetails),
  189. PinTouch = map.PinClicked
  190. };
  191. annotationView.Image = UIImage.FromFile(customPin.pinImage + ".png");
  192. return annotationView;
  193. }
  194. private CustomPin GetCustomPin(MKPointAnnotation mkAnnotation)
  195. {
  196. var position = new Position(mkAnnotation.Coordinate.Latitude, mkAnnotation.Coordinate.Longitude);
  197. foreach (var pin in map.CustomPins)
  198. {
  199. if (pin.pin.Position == position)
  200. {
  201. if (!string.IsNullOrEmpty(pin.pin.Label))
  202. {
  203. if (pin.pin.Label == mkAnnotation.Title)
  204. return pin;
  205. }
  206. }
  207. }
  208. return null;
  209. }
  210. [Foundation.Export("mapView:rendererForOverlay:")]
  211. MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlay)
  212. {
  213. if (polylineRenderer == null)
  214. {
  215. var o = ObjCRuntime.Runtime.GetNSObject(overlay.Handle) as MKPolyline;
  216. polylineRenderer = new MKPolylineRenderer(o);
  217. polylineRenderer.FillColor = UIColor.Blue;
  218. polylineRenderer.StrokeColor = UIColor.Blue;
  219. polylineRenderer.LineWidth = 2;
  220. polylineRenderer.Alpha = 0.4f;
  221. }
  222. return polylineRenderer;
  223. }
  224. private void UpdatePolyLine()
  225. {
  226. if (nativeMap == null)
  227. return;
  228. try
  229. {
  230. RemovePolyline();
  231. nativeMap.OverlayRenderer = GetOverlayRenderer;
  232. foreach (var routeCordinates in map.RouteCoordinates)
  233. {
  234. foreach (var positionEstimate in routeCordinates.PositionEstimateList)
  235. {
  236. int index = 0;
  237. CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[positionEstimate.actualPosition.Count];
  238. foreach (var position in positionEstimate.actualPosition)
  239. {
  240. coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
  241. index++;
  242. }
  243. routeOverlay = MKPolyline.FromCoordinates(coords);
  244. var o = ObjCRuntime.Runtime.GetNSObject(routeOverlay.Handle) as MKPolyline;
  245. polylineRenderer = new MKPolylineRenderer(o);
  246. var trackColor = GetColor(routeCordinates.trackColor);
  247. polylineRenderer.StrokeColor = trackColor;
  248. polylineRenderer.LineWidth = 1;
  249. nativeMap.AddOverlay(routeOverlay);
  250. _mkPolyLineList.Add(routeOverlay);
  251. }
  252. }
  253. }
  254. catch (Exception ex)
  255. {
  256. //Log exception to hockey app
  257. }
  258. }
  259. private UIColor GetColor(string color)
  260. {
  261. UIColor trackColor = UIColor.Orange;
  262. switch (color)
  263. {
  264. case "1":
  265. trackColor = UIColor.FromRGB(77, 123, 224);
  266. break;
  267. case "2":
  268. trackColor = UIColor.FromRGB(50, 193, 214);
  269. break;
  270. case "3":
  271. trackColor = UIColor.FromRGB(163, 178, 78);
  272. break;
  273. case "4":
  274. trackColor = UIColor.FromRGB(187, 93, 153);
  275. break;
  276. case "5":
  277. trackColor = UIColor.FromRGB(175, 98, 46);
  278. break;
  279. }
  280. return trackColor;
  281. }
  282. }
Now, we have everything in place. We can use TrackingMap.PinClicked and TrackingMap.OnVisibleRegionChanged in Xaml.cs to get the visibleRegion changed. And, we can bind our collections from ViewModel to the map so as to show pins and track information. It also allows redrawing the content as the collection changes.