I followed the Xamarin Tutorial Customizing a Map. I changed it a bit to be able to draw a route.
public class CustomMap : Map
{
    public static readonly BindableProperty RouteCoordinatesProperty =
    BindableProperty.Create(nameof(RouteCoordinates), typeof(List<Position>), typeof(CustomMap), new List<Position>(), BindingMode.TwoWay);
    public List<Position> RouteCoordinates
    {
        get { return (List<Position>)GetValue(RouteCoordinatesProperty); }
        set { SetValue(RouteCoordinatesProperty, value); }
    }
}
The WinPhone 8.1 renderer:
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace PROJECT.WinPhone
{
public class CustomMapRenderer : MapRenderer
{
    MapControl nativeMap;
    CustomMap formsMap;
    protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
    {
        base.OnElementChanged(e);
        if (e.OldElement != null)
        {
            nativeMap = Control as MapControl;
        }
        if (e.NewElement != null)
        {
            formsMap = (CustomMap)e.NewElement;
            nativeMap = Control as MapControl;
            //UpdatePolyLine();
            var coordinates = new List<BasicGeoposition>();
            foreach (var position in formsMap.RouteCoordinates)
            {
                coordinates.Add(new BasicGeoposition() { Latitude = position.Latitude, Longitude = position.Longitude });
            }
            var polyline = new MapPolyline();
            polyline.StrokeColor = Windows.UI.Color.FromArgb(128, 255, 0, 0);
            polyline.StrokeThickness = 5;
            polyline.Path = new Geopath(coordinates);
            nativeMap.MapElements.Add(polyline);
        }
    }
}
}
polyline.Path = new Geopath(coordinates); throws Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED))
The problem is that my two others renderer (Android & iOS) works.. Maybe something isn't possible because I work with WinPhone8.1 unlike the tutorial, which is UWP
AndroidRenderer
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace PROJECT.Droid
{
public class CustomMapRenderer : MapRenderer, IOnMapReadyCallback
{
    GoogleMap map;
    Polyline polyline;
    protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.View> e)
    {
        base.OnElementChanged(e);
        if (e.OldElement != null)
        {
            // Unsubscribe
        }
        if (e.NewElement != null)
        {
            ((MapView)Control).GetMapAsync(this);
        }
        UpdatePolyLine();
    }
    protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
        if (this.Element == null || this.Control == null)
            return;
        if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
        {
            UpdatePolyLine();
        }
    }
    private void UpdatePolyLine()
    {
        if (map != null)
        {
            if (polyline != null)
            {
                polyline.Remove();
                polyline.Dispose();
            }
            var polylineOptions = new PolylineOptions();
            polylineOptions.InvokeColor(0x66FF0000);
            int a = 0;
            foreach (var position in ((CustomMap)this.Element).RouteCoordinates)
            {
                polylineOptions.Add(new LatLng(position.Latitude, position.Longitude));
            }
            polyline = map.AddPolyline(polylineOptions);
        }
    }
    public void OnMapReady(GoogleMap googleMap)
    {
        map = googleMap;
        UpdatePolyLine();
    }
}
}
iOSRenderer
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace PROJECT.iOS
{
public class CustomMapRenderer : MapRenderer
{
    MKMapView nativeMap;
    MKPolylineRenderer polylineRenderer;
    protected override void OnElementChanged(ElementChangedEventArgs<View> e)
    {
        base.OnElementChanged(e);
        if (e.OldElement != null)
        {
            nativeMap = Control as MKMapView;
            nativeMap.OverlayRenderer = null;
        }
        if (e.NewElement != null)
        {
            UpdatePolyLine();
        }
    }
    protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
        if (this.Element == null || this.Control == null)
            return;
        if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
        {
            UpdatePolyLine();
        }
    }
    MKOverlayRenderer GetOverlayRenderer(MKMapView mapView, IMKOverlay overlay)
    {
        if (polylineRenderer == null)
        {
            polylineRenderer = new MKPolylineRenderer(overlay as MKPolyline);
            polylineRenderer.FillColor = UIColor.Blue;
            polylineRenderer.StrokeColor = UIColor.Red;
            polylineRenderer.LineWidth = 3;
            polylineRenderer.Alpha = 0.4f;
        }
        return polylineRenderer;
    }
    private void UpdatePolyLine()
    {
        if (nativeMap != null)
        {
            var formsMap = ((CustomMap)this.Element);
            nativeMap = Control as MKMapView;
            nativeMap.OverlayRenderer = GetOverlayRenderer;
            CLLocationCoordinate2D[] coords = new CLLocationCoordinate2D[formsMap.RouteCoordinates.Count];
            int index = 0;
            foreach (var position in formsMap.RouteCoordinates)
            {
                coords[index] = new CLLocationCoordinate2D(position.Latitude, position.Longitude);
                index++;
            }
            var routeOverlay = MKPolyline.FromCoordinates(coords);
            nativeMap.AddOverlay(routeOverlay);
        }
    }
}
}
 Emixam23
 Emixam23After a long time, I found why it didn't works...
If you're doing like me, if you do a HttpRequest to something as Google Direction API, then the result will comes after the first passage in the OnElementChanged().
Because formsMap.RouteCoordinates isn't null but empty, the Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED)) is thrown..
This is the good CustomMapRenderer for PolyLine use
using PROJECT;
using PROJECT.UWP;
using System.Collections.Generic;
using Windows.Devices.Geolocation;
using Windows.UI.Xaml.Controls.Maps;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Maps.UWP;
using Xamarin.Forms.Platform.UWP;
[assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
namespace PROJECT.UWP
{
    public class CustomMapRenderer : MapRenderer
    {
        MapControl nativeMap;
        CustomMap formsMap;
        protected override void OnElementChanged(ElementChangedEventArgs<Map> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement != null)
            {
                nativeMap = Control as MapControl;
            }
            if (e.NewElement != null)
            {
                formsMap = (CustomMap)e.NewElement;
                nativeMap = Control as MapControl;
                UpdatePolyLine();
            }
        }
        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (this.Element == null || this.Control == null)
                return;
            if (e.PropertyName == CustomMap.RouteCoordinatesProperty.PropertyName)
            {
                UpdatePolyLine();
            }
        }
        private void UpdatePolyLine()
        {
            if (formsMap != null && formsMap.RouteCoordinates.Count > 0)
            {
                List<BasicGeoposition> coordinates = new List<BasicGeoposition>();
                foreach (var position in formsMap.RouteCoordinates)
                {
                    coordinates.Add(new BasicGeoposition() { Latitude = position.Latitude, Longitude = position.Longitude });
                }
                Geopath path = new Geopath(coordinates);
                MapPolyline polyline = new MapPolyline();
                polyline.StrokeColor = Windows.UI.Color.FromArgb(128, 255, 0, 0);
                polyline.StrokeThickness = 5;
                polyline.Path = path;
                nativeMap.MapElements.Add(polyline);
            }
        }
    }
}
 Emixam23
 Emixam23User contributions licensed under CC BY-SA 3.0