Map Control mit bindable Pins erweitern

Aktuell hat die Implementierung des Map Controls aus dem Xamarin.Forms.Maps Package für mich ein kleines Manko. Es gibt zwar eine Property Pins zum Anzeigen von Push Pins auf der Karte, aber diese lässt sich nicht binden. In diesem Beitrag möchte ich zeigen, wie wir das bestehende Map Control erweitern können, damit wir die Pins auch binden können und die Daten so zum Beispiel über ein ViewModel berechnen oder vorhalten können.

Zunächst legen wir uns eine neues Xamarin.Forms Projekt in Visual Studio an und fügen dann in alle Projekte das NuGet-Package Xamarin.Forms.Maps hinzu. Nun müssen wir ein wenig plattformspezifischen Code schreiben, welcher die Karte initialisiert.

Für iOS öffnen wir die Datei AppDelegate.cs.  Hier finden wir in der Methode FinishedLaunching den Aufruf Xamarin.Forms.Forms.Init(). Direkt unterhalb dieser Zeile fügen wir nun Xamarin.FormsMaps.Init() hinzu.

Für UWP öffnen wir die Datei App.xaml.cs. Hier finden wir in der Methode OnLaunched() den Aufruf Xamarin.Forms.Forms.Init(e); und direkt darunter schreiben wir die Zeile Xamarin.FormsMaps.Init("BING_MAPS_KEY") und müssen hier nun einen Bing-Maps Key eintragen, welchen man hier beantragen kann.

Für Android öffnen wir zunächst die Datei MainActivity.cs und hier steht in der Methode OnCreate() der Aufruf Xamarin.Forms.Forms.Init(this, bundle). Darunter fügen wir auch hier Xamarin.FormsMaps.Init(this, bundle) ein. Anschließend öffnen wir unter Properties die Datei AndroidManifest.xml. Innerhalb des application-Tags fügen wir die beiden Zeilen

<meta-data android:name="com.google.android.geo.API_KEY" android:value="YOUR_GOOGLEMAPS_KEY" />
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />

hinzu. Auch hier muss der Key durch einen Google Maps Key ersetzt werden. Einen Key kann man sich hier erzeugen lassen.

Nun wollen wir unsere ExtendedMap entwickeln, welche es ermöglicht, dass wir später eine PushPin Property haben, welche wir binden können. In dem Portable Projekt lege ich hierfür einen Ordner Controls an und darin eine Klasse mit dem Namen ExtendedMap. Diese Klasse leitet nun von der Klasse Map im Namespace Xamarin.Forms.Maps ab.

Anschließend fügen wir den folgenden Code hinzu:

public class ExtendedMap : Map
{
    public static readonly BindableProperty PushPinsProperty =
        BindableProperty.Create(nameof(PushPins), typeof(IEnumerable<Pin>), typeof(ExtendedMap), Enumerable.Empty<Pin>(), propertyChanged: OnPushPinsChanged);

    public IEnumerable<Pin> PushPins
    {
      get => (IEnumerable<Pin>)GetValue(PushPinsProperty);
      set => SetValue(PushPinsProperty, value);
    }

    private static void OnPushPinsChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
      if (!(bindable is ExtendedMap map))
        return;
      map.UpdatePushPins((IEnumerable<Pin>)newvalue);
    }

    private void UpdatePushPins(IEnumerable<Pin> pins)
    {
      Pins.Clear();
      foreach (var pin in pins)
        Pins.Add(pin);
    }
}

Wir haben eine neue BindableProperty PushPins hinzufügt, welche beim Ändern entsprechend die Pins der Karte hinzufügt. Nun können wir die ExtendedMap bereits verwenden.

Ich habe auf GitHub eine kleine Beispiel-App veröffentlicht, welche die Verwendung der ExtendedMap noch einmal verdeutlicht. Der Aufwand ist wirklich sehr gering, weil keine zusätzlichen Renderer geschrieben werden müssen, da wir das bestehende Map Control nur erweitert haben.

StatusBar unter Android, iOS und UWP einfärben Heap Size bei Xamarin-Projekten erhöhen Xamarin.Forms Projekt mit .NET Standard