question

FerryCathal-2954 avatar image
0 Votes"
FerryCathal-2954 asked RobertSullivan-2031 commented

Xamarin Custompins query

Hello
Ive been experimenting with xamarin form maps and followed the docuemntation and some tutorials on creating a custom renderer for Android.
I successfully got the custom pins example to work for the single pin example in for the San Francisco microsoft office.
However, i am loading a bunch of pins from a JSON file i took from an online example.
That all works and my custom icon is shown on the map.
When i click any pin however, i get a null exception for customPins.

I am not 100% sure what is wrong but all i really want to do with the pins is literally on click show a view that has a bit of text and an image.

Ive been debugging for a while now and just cant quite figure out why my multiple pins code doesnt work as intended. Below is my custom map renderer for Android.

 using Android.Content;
 using Android.Gms.Maps;
 using Android.Gms.Maps.Model;
 using Android.Widget;
 using MAPS;
 using MAPS.Droid;
 using System;
 using System.Collections.Generic;
 using Xamarin.Forms;
 using Xamarin.Forms.Maps;
 using Xamarin.Forms.Maps.Android;
 [assembly: ExportRenderer(typeof(CustomMap), typeof(CustomMapRenderer))]
 namespace MAPS.Droid
 {
     public class CustomMapRenderer : Xamarin.Forms.Maps.Android.MapRenderer, GoogleMap.IInfoWindowAdapter
     {
         List<CustomPin> customPins;
    
         public CustomMapRenderer(Context context) : base(context)
         {
         }
         protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e)
         {
             base.OnElementChanged(e);
    
             if (e.OldElement != null)
             {
                 NativeMap.InfoWindowClick -= OnInfoWindowClick;
             }
    
             if (e.NewElement != null)
             {
                 var formsMap = (CustomMap)e.NewElement;
                 customPins = formsMap.CustomPins;
             }
         }
         protected override void OnMapReady(GoogleMap map)
         {
             base.OnMapReady(map);
    
             NativeMap.InfoWindowClick += OnInfoWindowClick;
             NativeMap.SetInfoWindowAdapter(this);
         }
    
         protected override MarkerOptions CreateMarker(Pin pin)
         {
             var marker = new MarkerOptions();
    
             CustomPin p = new CustomPin();
             foreach (var cp in customPins)
             {
                 if (cp.Position == pin.Position)
                 {
                     p = cp;
                 }
             }
             marker.SetPosition(new LatLng(pin.Position.Latitude, pin.Position.Longitude));
             marker.SetTitle(pin.Label);
          //   marker.SetSnippet(pin.Address);
         //   marker.SetIcon(BitmapDescriptorFactory.FromFile(p.icon));
          marker.SetIcon(BitmapDescriptorFactory.FromResource(Resource.Drawable.pin));
             return marker;
         }
    
         void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
         {
             var customPin = GetCustomPin(e.Marker);
             if (customPin == null)
             {
                 throw new Exception("Custom pin not found");
             }
    
             if (!string.IsNullOrWhiteSpace(customPin.Url))
             {
                 var url = Android.Net.Uri.Parse(customPin.Url);
                 var intent = new Intent(Intent.ActionView, url);
                 intent.AddFlags(ActivityFlags.NewTask);
                 Android.App.Application.Context.StartActivity(intent);
             }
         }
    
    
    
    
    
         //}
    
    
         public Android.Views.View GetInfoContents(Marker marker)
         {
    
             var inflater = Android.App.Application.Context.GetSystemService(Context.LayoutInflaterService) as Android.Views.LayoutInflater;
             if (inflater != null)
             {
                 Android.Views.View view;
    
                 var customPin = GetCustomPin(marker);
                 if (customPin == null)
                 {
                 //    throw new Exception("Custom pin not found");
                 }
    
                 if (customPin.Name.Equals("Xamarin"))
                 {
                     view = inflater.Inflate(Resource.Layout.XamarinMapInfoWindow, null);
                 }
                 else
                 {
                     view = inflater.Inflate(Resource.Layout.MapInfoWindow, null);
                 }
    
                 var infoTitle = view.FindViewById<TextView>(Resource.Id.InfoWindowTitle);
                 var infoSubtitle = view.FindViewById<TextView>(Resource.Id.InfoWindowSubtitle);
    
                 if (infoTitle != null)
                 {
                     infoTitle.Text = marker.Title;
                 }
                 if (infoSubtitle != null)
                 {
                     infoSubtitle.Text = marker.Snippet;
                 }
    
                 return view;
             }
             return null;
         }
         //public Android.Views.View GetInfoContents(Marker marker)
         //{
         //    throw new NotImplementedException();
         //}
    
         public Android.Views.View GetInfoWindow(Marker marker)
         {
             return null;
         }
    
         CustomPin GetCustomPin(Marker annotation)
         {
             var position = new Position(annotation.Position.Latitude, annotation.Position.Longitude);
             foreach (var pin in customPins)
             {
                 if (pin.Position == position)
                 {
                     return pin;
                 }
             }
             return null;
         }
    
    
    
    
    
     }
          
 }

And below is my aboutpage form, there are some background gps and other things in there that can be ignored.

 using System;
 using System.IO;
 using System.Reflection;
 using Xamarin.Forms;
 using Newtonsoft.Json;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Threading.Tasks;
 using Xamarin.Forms.Maps;
 using Xamarin.Essentials;
 using Distance = Xamarin.Forms.Maps.Distance;
 using Google.Protobuf.WellKnownTypes;
 using static Google.Protobuf.Reflection.FieldDescriptorProto.Types;
    
 namespace MAPS.Views
 {
     public partial class AboutPage : ContentPage
     {
         IlocationUpdateService loc;
        
         public AboutPage()
         {
             InitializeComponent();
             Task.Delay(2000);
             UpdateMap();
         }
         async void OnActionSheetCancelDeleteClicked()
         {
             bool answer = await DisplayAlert("Location Request", "Please enable location services to use this app", "Settings", "Cancel");
             if (answer == true)
             {
                 DependencyService.Get<ILocSettings>().OpenSettings();
             }
         }
         protected override void OnAppearing()
         {
             base.OnAppearing();
             bool gpsStat = DependencyService.Get<ILocSettings>().isGpsAvailable();
             if (gpsStat == false)
             {
                 OnActionSheetCancelDeleteClicked();
             }
    
             loc = DependencyService.Get<IlocationUpdateService>();
    
             loc.LocationChanged += (object sender, ILocationEventArgs args) =>
               {
    
                   String lat1 = args.Latitude.ToString();
                   String lng1 = args.Longitude.ToString();
               //   UpdateMap();
               };
             loc.GetUsedLocation();
    
         }
         protected override void OnDisappearing()
         {
             base.OnDisappearing();
             loc = null;
    
         }
         List<Place> placesList = new List<Place>();
         private async void UpdateMap()
         {
             var assembly = IntrospectionExtensions.GetTypeInfo(typeof(AboutPage)).Assembly;
                 Stream stream = assembly.GetManifestResourceStream("MAPS.Places.json");
                 string text = string.Empty;
                 using (var reader = new StreamReader(stream))
                 {
                     text = reader.ReadToEnd();
                 }
                 var resultObject = JsonConvert.DeserializeObject<Places>(text);
                 var request = new Xamarin.Essentials.GeolocationRequest(GeolocationAccuracy.Best, TimeSpan.FromSeconds(30));
                 var location = await Geolocation.GetLocationAsync(request);
             CustomMap customMap = new CustomMap()
             {
                 IsShowingUser = true
    
             };
             customMap.CustomPins = new List<CustomPin>(); // put this before the foreach
               
             foreach (var place in resultObject.results)
             {
                 CustomPin pin = new CustomPin()
             {
                        
                 Type = PinType.Place,
                 Position = new Position(place.geometry.location.lat, place.geometry.location.lng),
                 Label = place.name,
                 Address = place.vicinity,
                 Name = "Xamarin",
                 icon = "pin.png",
                 Url = "http://xamarin.com/about/"
             };
          
             customMap.Pins.Add(pin);
             }
                 customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(location.Latitude, location.Longitude), Distance.FromKilometers(0.4))); ;
             Content = customMap;

Thanks guys, much appreciated!

dotnet-xamarin
· 6
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

i am loading a bunch of pins from a JSON file i took from an online example.

Hi, FerryCathal. Will it work fine if creating the custom pin directly as below?

CustomPin pin = new CustomPin
{
    Type = PinType.Place,
    Position = new Position(37.79752, -122.40183),
    Label = "Xamarin San Francisco Office",
    Address = "394 Pacific Ave, San Francisco CA",
    Name = "Xamarin",
    Url = "http://xamarin.com/about/"
};
customMap.CustomPins = new List<CustomPin> { pin };
customMap.Pins.Add(pin);
customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(37.79752, -122.40183), Distance.FromMiles(1.0)));

Someone faced the similar issue, it's caused by the data cast. Please check: https://github.com/xamarin/Xamarin.Forms/issues/6393

0 Votes 0 ·

The problem is with the example code source: CustomMapRenderer.cs

If you load your pins after the view is shown, this code: GetCustomPin(Marker annotation)
will always return null for the new pins (since the new pin[s] haven't been assigned to customPins) because OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Map> e) only gets called when the map is created (this is the only place map pins gets added to the customPins list).

The custom pin will get created because CreateMarker works, but when you touch the pin you get an exception error since the customPin is null...

Question is, how to add pins AFTER the view is loaded using this custom renderer?


0 Votes 0 ·

To see an example, add this modified code to MapPage.xaml:

        <StackLayout>
         <Button Text="Refresh" Clicked="Button_Clicked" HorizontalOptions="Center" VerticalOptions="Start"/>
         <local:CustomMap x:Name="customMap"
                          MapType="Street" />
         </StackLayout>
0 Votes 0 ·

Then add this code to MapPage.xaml.cs:

          private void Button_Clicked(object sender, System.EventArgs e)
             {
                 CustomPin pin = new CustomPin
                 {
                     Type = PinType.Place,
                     Position = new Position(37.78000, -122.39217),
                     Label = "Hyatt Place San Francisco",
                     Address = "701 3rd St, San Francisco, CA 94107",
                     Name = "Hyatt",
                     Url = "http://hyatt.com"
                 };
                 customMap.CustomPins = new List<CustomPin> { pin };
                 customMap.Pins.Add(pin);            
                 customMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(37.79752, -122.40183), Distance.FromMiles(10)));
             }
0 Votes 0 ·
Show more comments

0 Answers