Add separate MarkerClick and InfoWindowClick events for Pins (#6079)
authorJoe Manke <jmanke1227@gmail.com>
Wed, 14 Aug 2019 00:41:11 +0000 (19:41 -0500)
committerSamantha Houts <samhouts@users.noreply.github.com>
Wed, 14 Aug 2019 00:41:11 +0000 (17:41 -0700)
* Add separate MarkerClick and InfoWindowClick events for Pins

* Mark Pin.Clicked obsolete

* Use for loops to look up Pins in renderers.

* Add custom EventArgs for MapClicked and InfoWindowClicked

* Update MapGalleryPage for new events

* Obsolete messages and other cleanup

* Correct the pragma when calling Pin.SendTapped

* Change obsolete message to say 4.2.0

* Ignore warning on Pin.SendTap() on other platforms

* Update obsolete messages to 4.3.0

Co-Authored-By: Samantha Houts <samhouts@users.noreply.github.com>
fixes #2509
fixes #3131
fixes #5490

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla29017.cs
Xamarin.Forms.Controls/GalleryPages/MapGallery.xaml
Xamarin.Forms.Controls/GalleryPages/MapGallery.xaml.cs
Xamarin.Forms.Maps.Android/MapRenderer.cs
Xamarin.Forms.Maps.GTK/MapRenderer.cs
Xamarin.Forms.Maps.Tizen/MapRenderer.cs
Xamarin.Forms.Maps.UWP/PushPin.cs
Xamarin.Forms.Maps.WPF/FormsPushPin.cs
Xamarin.Forms.Maps.iOS/MapRenderer.cs
Xamarin.Forms.Maps/Pin.cs
Xamarin.Forms.Maps/PinClickedEventArgs.cs [new file with mode: 0644]

index 83a6436..bcbeccf 100644 (file)
@@ -31,9 +31,10 @@ namespace Xamarin.Forms.Controls.Issues
                                        new Button {
                                                Text = "Add pins",
                                                Command = new Command (() => {
-
                                                        foreach (var pin in map.Pins) {
+#pragma warning disable CS0618
                                                                pin.Clicked -= PinClicked;
+#pragma warning restore CS0618
                                                        }
 
                                                        map.Pins.Clear ();
@@ -54,8 +55,9 @@ namespace Xamarin.Forms.Controls.Issues
                                                                        Type = PinType.Place,
                                                                        Position = new Position (map.VisibleRegion.Center.Latitude + lat, map.VisibleRegion.Center.Longitude + lng)
                                                                };
-
+#pragma warning disable CS0618
                                                                pin.Clicked += PinClicked;
+#pragma warning restore CS0618
                                                                map.Pins.Add (pin);
                                                        }
                                                })
index 8ab68a9..e1b8539 100644 (file)
@@ -10,6 +10,7 @@
             <RowDefinition Height="*" />
             <RowDefinition Height="Auto" />
             <RowDefinition Height="Auto" />
+            <RowDefinition Height="Auto" />
             <RowDefinition Height="*" />
         </Grid.RowDefinitions>
 
                    Placeholder="Search Address"
                    SearchButtonPressed="SearchForAddress" />
 
-        <Label x:Name="LastPinClickLabel"
-               Grid.Row="2" />
-        <Label x:Name="LastMapClickLabel"
-               Grid.Row="3" />
-        <ScrollView Grid.Row="4">
+        <Label
+            x:Name="LastMarkerClickLabel"
+            Grid.Row="2" />
+        <Label
+            x:Name="LastInfoWindowClickLabel"
+            Grid.Row="3" />
+        <Label
+            x:Name="LastMapClickLabel"
+            Grid.Row="4" />
+        <ScrollView Grid.Row="5">
             <StackLayout>
                 <Button Clicked="MapTypeClicked"
                         Text="Map Type" />
index 139df7f..b10d4b0 100644 (file)
@@ -23,7 +23,11 @@ namespace Xamarin.Forms.Controls
                        InitializeComponent();
 
                        Map = MakeMap();
-                       Map.Pins.ForEach(pin => pin.Clicked += PinClicked);
+                       Map.Pins.ForEach(pin =>
+                       {
+                               pin.MarkerClicked += MarkerClicked;
+                               pin.InfoWindowClicked += InfoWindowClicked;
+                       });
                        Map.MapClicked += MapClicked;
 
                        ((Grid)Content).Children.Add(Map, 0, 1);
@@ -63,9 +67,15 @@ namespace Xamarin.Forms.Controls
                        };
                }
 
-               void PinClicked(object sender, EventArgs e)
+               void MarkerClicked(object sender, PinClickedEventArgs e)
+               {
+                       LastMarkerClickLabel.Text = $"Last Marker Clicked: {((Pin)sender).Label}";
+               }
+
+               void InfoWindowClicked(object sender, PinClickedEventArgs e)
                {
-                       LastPinClickLabel.Text = $"Last Pin Clicked: {((Pin)sender).Label}";
+                       LastInfoWindowClickLabel.Text = $"Last Info Window Clicked: {((Pin)sender).Label}";
+                       e.HideInfoWindow = true;
                }
 
                async void SearchForAddress(object sender, EventArgs e)
index 93eba49..168cbb8 100644 (file)
@@ -85,7 +85,8 @@ namespace Xamarin.Forms.Maps.Android
                                {
                                        NativeMap.MyLocationEnabled = false;
                                        NativeMap.SetOnCameraMoveListener(null);
-                                       NativeMap.InfoWindowClick -= MapOnMarkerClick;
+                                       NativeMap.MarkerClick -= OnMarkerClick;
+                                       NativeMap.InfoWindowClick -= OnInfoWindowClick;
                                        NativeMap.MapClick -= OnMapClick;
                                        NativeMap.Dispose();
                                        NativeMap = null;
@@ -123,7 +124,8 @@ namespace Xamarin.Forms.Maps.Android
                                if (NativeMap != null)
                                {
                                        NativeMap.SetOnCameraMoveListener(null);
-                                       NativeMap.InfoWindowClick -= MapOnMarkerClick;
+                                       NativeMap.MarkerClick -= OnMarkerClick;
+                                       NativeMap.InfoWindowClick -= OnInfoWindowClick;
                                        NativeMap.MapClick -= OnMapClick;
                                        NativeMap = null;
                                }
@@ -206,7 +208,8 @@ namespace Xamarin.Forms.Maps.Android
                        }
 
                        map.SetOnCameraMoveListener(this);
-                       map.InfoWindowClick += MapOnMarkerClick;
+                       map.MarkerClick += OnMarkerClick;
+                       map.InfoWindowClick += OnInfoWindowClick;
                        map.MapClick += OnMapClick;
 
                        map.UiSettings.ZoomControlsEnabled = Map.HasZoomEnabled;
@@ -282,28 +285,58 @@ namespace Xamarin.Forms.Maps.Android
                        return _markers?.Find(m => m.Id == (string)pin.MarkerId);
                }
 
-               void MapOnMarkerClick(object sender, GoogleMap.InfoWindowClickEventArgs eventArgs)
+               protected Pin GetPinForMarker(Marker marker)
                {
-                       // clicked marker
-                       var marker = eventArgs.Marker;
-
-                       // lookup pin
                        Pin targetPin = null;
-                       for (var i = 0; i < Map.Pins.Count; i++)
+
+                       for (int i = 0; i < Map.Pins.Count; i++)
                        {
-                               Pin pin = Map.Pins[i];
-                               if ((string)pin.MarkerId != marker.Id)
+                               var pin = Map.Pins[i];
+                               if ((string)pin.MarkerId == marker.Id)
                                {
-                                       continue;
+                                       targetPin = pin;
+                                       break;
                                }
+                       }
+
+                       return targetPin;
+               }
+
+               void OnMarkerClick(object sender, GoogleMap.MarkerClickEventArgs e)
+               {
+                       var pin = GetPinForMarker(e.Marker);
+
+                       if (pin == null)
+                       {
+                               return;
+                       }
+
+                       // Setting e.Handled = true will prevent the info window from being presented
+                       // SendMarkerClick() returns the value of PinClickedEventArgs.HideInfoWindow
+                       bool handled = pin.SendMarkerClick();
+                       e.Handled = handled;
+               }
+
+               void OnInfoWindowClick(object sender, GoogleMap.InfoWindowClickEventArgs e)
+               {
+                       var marker = e.Marker;
+                       var pin = GetPinForMarker(marker);
 
-                               targetPin = pin;
-                               break;
+                       if (pin == null)
+                       {
+                               return;
                        }
 
-                       // only consider event handled if a handler is present.
-                       // Else allow default behavior of displaying an info window.
-                       targetPin?.SendTap();
+#pragma warning disable CS0618
+                       pin.SendTap();
+#pragma warning restore CS0618
+
+                       // SendInfoWindowClick() returns the value of PinClickedEventArgs.HideInfoWindow
+                       bool hideInfoWindow = pin.SendInfoWindowClick();
+                       if (hideInfoWindow)
+                       {
+                               marker.HideInfoWindow();
+                       }
                }
 
                void OnMapClick(object sender, GoogleMap.MapClickEventArgs e)
index 237d542..956e334 100644 (file)
@@ -418,8 +418,10 @@ namespace Xamarin.Forms.Maps.GTK
                 if(pin.Position.Latitude == marker.Position.Lat &&
                     pin.Position.Longitude == marker.Position.Lng)
                 {
-                    pin.SendTap();
-                    break;
+#pragma warning disable CS0618
+                       pin.SendTap();
+#pragma warning restore CS0618
+                                       break;
                 }
             }
         }
index 15363b3..14a8baf 100644 (file)
@@ -207,7 +207,9 @@ namespace Xamarin.Forms.Maps.Tizen
                                pin.MarkerId = nativePin;
                                nativePin.Clicked += (s, e) =>
                                {
+#pragma warning disable CS0618
                                        pin.SendTap();
+#pragma warning restore CS0618
                                };
                                Control.Add(nativePin);
                                _pins.Add(pin);
index 3acb485..64cb0f0 100644 (file)
@@ -46,7 +46,10 @@ namespace Xamarin.Forms.Maps.UWP
 
                void PushPinTapped(object sender, TappedRoutedEventArgs e)
                {
+#pragma warning disable CS0618
                        _pin.SendTap();
+#pragma warning restore CS0618
+                       _pin.SendMarkerClick();
                }
 
                void UpdateLocation()
index 3033591..8a10280 100644 (file)
@@ -36,7 +36,9 @@ namespace Xamarin.Forms.Maps.WPF
 
                void FormsPushPin_MouseDown(object sender, MouseButtonEventArgs e)
                {
+#pragma warning disable CS0618
                        Pin.SendTap();
+#pragma warning restore CS0618
                }
 
                void PinPropertyChanged(object sender, PropertyChangedEventArgs e)
index 795ad57..684912e 100644 (file)
@@ -1,6 +1,4 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
+using System.Collections;
 using System.Collections.ObjectModel;
 using System.Collections.Specialized;
 using System.ComponentModel;
@@ -8,7 +6,6 @@ using CoreLocation;
 using MapKit;
 using ObjCRuntime;
 using RectangleF = CoreGraphics.CGRect;
-using Foundation;
 
 #if __MOBILE__
 using UIKit;
@@ -71,6 +68,7 @@ namespace Xamarin.Forms.Maps.MacOS
                                }
 
                                var mkMapView = (MKMapView)Control;
+                               mkMapView.DidSelectAnnotationView -= MkMapViewOnAnnotationViewSelected;
                                mkMapView.RegionChanged -= MkMapViewOnRegionChanged;
                                mkMapView.GetViewForAnnotation = null;
                                if (mkMapView.Delegate != null)
@@ -145,6 +143,7 @@ namespace Xamarin.Forms.Maps.MacOS
                                        SetNativeControl(mapView);
 
                                        mapView.GetViewForAnnotation = GetViewForAnnotation;
+                                       mapView.DidSelectAnnotationView += MkMapViewOnAnnotationViewSelected;
                                        mapView.RegionChanged += MkMapViewOnRegionChanged;
 #if __MOBILE__
                                        mapView.AddGestureRecognizer(_mapClickedGestureRecognizer = new UITapGestureRecognizer(OnMapClicked));
@@ -242,7 +241,7 @@ namespace Xamarin.Forms.Maps.MacOS
                        }
 
 #if __MOBILE__
-                       var recognizer = new UITapGestureRecognizer(g => OnClick(annotation, g))
+                       var recognizer = new UITapGestureRecognizer(g => OnCalloutClicked(annotation))
                        {
                                ShouldReceiveTouch = (gestureRecognizer, touch) =>
                                {
@@ -251,33 +250,50 @@ namespace Xamarin.Forms.Maps.MacOS
                                }
                        };
 #else
-                       var recognizer = new NSClickGestureRecognizer(g => OnClick(annotation, g));
+                       var recognizer = new NSClickGestureRecognizer(g => OnCalloutClicked(annotation));
 #endif
                        mapPin.AddGestureRecognizer(recognizer);
                }
 
-#if __MOBILE__
-               void OnClick(object annotationObject, UITapGestureRecognizer recognizer)
-#else
-               void OnClick(object annotationObject, NSClickGestureRecognizer recognizer)
-#endif
+               protected Pin GetPinForAnnotation(IMKAnnotation annotation)
                {
-                       // https://bugzilla.xamarin.com/show_bug.cgi?id=26416
-                       NSObject annotation = Runtime.GetNSObject(((IMKAnnotation)annotationObject).Handle);
-                       if (annotation == null)
-                               return;
-
-                       // lookup pin
                        Pin targetPin = null;
-                       foreach (Pin pin in ((Map)Element).Pins)
+                       var map = (Map)Element;
+
+                       for (int i = 0; i < map.Pins.Count; i++)
                        {
-                               object target = pin.MarkerId;
-                               if (target != annotation)
-                                       continue;
+                               var pin = map.Pins[i];
+                               if ((IMKAnnotation)pin.MarkerId == annotation)
+                               {
+                                       targetPin = pin;
+                                       break;
+                               }
+                       }
 
-                               targetPin = pin;
-                               break;
+                       return targetPin;
+               }
+
+               void MkMapViewOnAnnotationViewSelected(object sender, MKAnnotationViewEventArgs e)
+               {
+                       var annotation = e.View.Annotation;
+                       var pin = GetPinForAnnotation(annotation);
+
+                       if (pin != null)
+                       {
+                               // SendMarkerClick() returns the value of PinClickedEventArgs.HideInfoWindow
+                               // Hide the info window by deselecting the annotation
+                               bool deselect = pin.SendMarkerClick();
+                               if (deselect)
+                               {
+                                       ((MKMapView)Control).DeselectAnnotation(annotation, false);
+                               }
                        }
+               }
+               
+               void OnCalloutClicked(IMKAnnotation annotation)
+               {
+                       // lookup pin
+                       Pin targetPin = GetPinForAnnotation(annotation);
 
                        // pin not found. Must have been activated outside of forms
                        if (targetPin == null)
@@ -288,7 +304,17 @@ namespace Xamarin.Forms.Maps.MacOS
                        if (_lastTouchedView is MKAnnotationView)
                                return;
 
+#pragma warning disable CS0618
                        targetPin.SendTap();
+#pragma warning restore CS0618
+
+                       // SendInfoWindowClick() returns the value of PinClickedEventArgs.HideInfoWindow
+                       // Hide the info window by deselecting the annotation
+                       bool deselect = targetPin.SendInfoWindowClick();
+                       if (deselect)
+                       {
+                               ((MKMapView)Control).DeselectAnnotation(annotation, true);
+                       }
                }
 
 #if __MOBILE__
index 747eb0f..e7a80cb 100644 (file)
@@ -65,8 +65,13 @@ namespace Xamarin.Forms.Maps
                        }
                }
 
+               [Obsolete("This event is obsolete as of 4.3.0. Please use MarkerClicked and/or InfoWindowClicked instead.")]
                public event EventHandler Clicked;
 
+               public event EventHandler<PinClickedEventArgs> MarkerClicked;
+
+               public event EventHandler<PinClickedEventArgs> InfoWindowClicked;
+
                public override bool Equals(object obj)
                {
                        if (ReferenceEquals(null, obj))
@@ -101,6 +106,7 @@ namespace Xamarin.Forms.Maps
                }
 
                [EditorBrowsable(EditorBrowsableState.Never)]
+               [Obsolete("This method is obsolete as of 4.3.0.")]
                public bool SendTap()
                {
                        EventHandler handler = Clicked;
@@ -111,6 +117,22 @@ namespace Xamarin.Forms.Maps
                        return true;
                }
 
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public bool SendMarkerClick()
+               {
+                       var args = new PinClickedEventArgs();
+                       MarkerClicked?.Invoke(this, args);
+                       return args.HideInfoWindow;
+               }
+
+               [EditorBrowsable(EditorBrowsableState.Never)]
+               public bool SendInfoWindowClick()
+               {
+                       var args = new PinClickedEventArgs();
+                       InfoWindowClicked?.Invoke(this, args);
+                       return args.HideInfoWindow;
+               }
+
                bool Equals(Pin other)
                {
                        return string.Equals(Label, other.Label) && Equals(Position, other.Position) && Type == other.Type && string.Equals(Address, other.Address);
diff --git a/Xamarin.Forms.Maps/PinClickedEventArgs.cs b/Xamarin.Forms.Maps/PinClickedEventArgs.cs
new file mode 100644 (file)
index 0000000..8662af7
--- /dev/null
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Xamarin.Forms.Maps
+{
+       public class PinClickedEventArgs : EventArgs
+       {
+               public bool HideInfoWindow { get; set; }
+
+               public PinClickedEventArgs()
+               {
+                       HideInfoWindow = false;
+               }
+       }
+}