Xamarin Forms mit Custom Renderern erweitern

Ich befinde mich gerade auf der Xamarin Evolve Konferenz. Im Rahmen der Konferenz habe ich zum ersten mal mit den Xamarin Forms gespielt – einer Art UI Abstraktion für Cross Platform Entwicklung. Die kurze Story: Wer Xamarin Forms verwendet, der kann nicht nur App Logik teilen, sondern für den ist es auch möglich den UI Code zu teilen. Xamarin Forms kümmert sich dann darum, dass das UI auf den Zielplattformen so aussieht, wie es aussehen soll. Aus einem TabControl auf Android wird dann zum Beispiel ein Pivot Control auf Windows Phone.

Nun stellt sich natürlich immer die Frage, wie weit das ausreicht, was Out-Of-The-Box kommt. Und wie man gegebenenfalls erweitern kann. Wer meine Demo Apps kennt, der weiß, dass ich gerne mal mit Landkarten in Apps arbeite. Dabei habe ich im Falle von Xamarin Forms festgestellt, dass es nicht ohne Weiteres möglich ist, etwas anderes als den “Pin” auf die Karte zu zeichnen. Eigene Objekte, Bilder etc. funktionieren nicht ohne eigenes Zutun.

Das kommt mir wie gerufen, da ich hier den Custom Renderer gleich ausprobieren kann. So geht’s Schritt für Schritt:

Zunächst müssen wir festlegen, welches Element in Xamarin Forms eigentlich einen neuen Renderer bekommen soll. Die Vermutung liegt nahe, dass man hier einfach den Pin heranzieht. Leider entpuppt sich das als schlechte Idee, da von Pin nicht abgeleitet werden kann und wir demzufolge auch den Renderer für den Pin nicht einfach überbügeln können.

Als nächstes kommt das Map Element in Frage. Das funktioniert doch gleich viel besser, wir können als eine Klasse MyMap erstellen, die von Map ableitet:

image

Diese MyMap Klasse können wir dann in unserem Xamarin Forms Xaml verwenden:

image

Um jetzt die Darstellung der Pins auf der Karte abzuändern, müssen wir zunächst Pins zur Karte hinzufügen. Das geschieht im C# Code. (Den größten Teil dieses Codes hab ich einfach von einem Xamarin Sample übernommen.) Wir legen hier ein neues Pin-Objekt an und fügen ein paar Properties hinzu. Dann fügen wir den Pin der Map zu.

image

Die Map hat jetzt einen Pin. Wir müssen also nur noch dafür sorgen, dass sich die Darstellung ändert. Dafür legen wir eine neue Klasse MyMapRenderer in den jeweilig plattformspezifischen Projekten an. Das ist wichtig! Es muss ein plattformspezifische Implementierung der Renderer geben, da genau an dieser Stelle die Xamarin Forms ja keine Vereinheitlichung bieten.

image

Die Implementierung der Klasse müssen wir mit dem Attribut ExportRenderer dekorieren. Damit zeigen sorgen wir dafür, dass der Renderer zur Laufzeit auch gefunden wird. Im Attribut geben wir den Typ des Elements an, für den wir den Renderer schreiben und den Typen des Renderers selbst. Danach überschreiben wir die OnElementChanged Methode. Die Implementierung dieser Methode gibt uns die Möglichkeit in die Darstellung plattformspezifisch einzugreifen.

image

Jetzt wird’s spannend: Über Element können wir auf das MyMap Objekt zugreifen – also auf das Element, dass wir im Xamarin.Forms XAML angelegt haben. Über Control erhalten wir zugriff auf das tatscählich gerenderte Control, welches plattformspezifisch ist und in diesem Falle vom Typen Microsoft.Phone.Maps.Controls.Map.

image

 

Hier können wir jetzt nach belieben Layers auf die Karte zeichnen – ganz so, wie es die plattformspezifische API eben vorgibt. Wenn wir das tun, dann wird aber der “alte” Pin trotzdem noch gezeichnet. Deshalb ist es sinnvoller, unsere eigenen Pins in einer eigenen Property von MyMap zu speichern. Dazu legen wir eine Liste mit Pins an:

image

Und fügen dann den Pin lieber dieser Liste zu:

image

Anschließend lesen wir einfach mal das erste Element in dieser Pinliste im MyMapRenderer aus (wir wissen ja, dass wir was reingeschrieben haben).

image

Diesen Pin zeichnen wir dann über die Methode AddPinToMap dann auf unser Mapcontrol . Bei der Implementierung dieser Methode verwenden wir den plattformspezifischen Windows Phone Code.

Der kann zum Beispiel so aussehen – für Android sieht er aber anders aus, da wir da gegen Google Play Service API implementieren würden.

image

F5 beweist, dass das auch funktioniert:  Ein Apfel in den Windows Phone Maps. Eindeutig, oder?

image

Ich bin begeistert. Unterm Strich war das wirklich unerwartet easy. So bald ich Zeit habe, stelle ich die ganze Solution als kleine Demo auf Codeplex.