
Dieser Artikel ist eine gemeinsame Arbeit von Giulien und mir. Während er sich hauptsächlich um die App und den grundlegenden iOS-Handler kümmerte, habe ich die Dokumentation, den Android-Handler und die erweiterte iOS Map-Handler-Implementierung erstellt. In diesem Artikel werden wir die folgenden Themen behandeln:
Wie verwendet man die eingebaute Kartenunterstützung?
Wenn Sie einfach nur eine Karte anzeigen möchten, reicht es aus, die integrierte Lösung mit .NET7-Unterstützung zu verwenden. Dazu müssen Sie nur die folgenden drei Schritte ausführen:
- Fügen Sie das NuGet-Paket Microsoft.Maui.Controls.Maps zu Ihrem Projekt hinzu.
- Fügen Sie das Kartensteuerelement zu Ihrer Seite hinzu.
- Registrieren Sie die Handler, indem Sie UseMauiMaps() in MauiProgram.cs hinzufügen. Öffnen Sie dazu MauiProgram.cs und fügen Sie
UseMauiMaps()
zum Builder hinzu.
Wenn Ihnen dieser Ansatz nicht ausreicht, lesen Sie weiter und erstellen Sie Ihren eigenen Google Maps und Apple Maps Handler.
Wenn Sie ein Projekt in Xamarin oder .NET MAUI haben, können wir Ihnen Zeit sparen.
Bevor Sie alles selbst in die Hand nehmen, werfen Sie einen Blick auf unsere Dienstleistungen. Unser Team aus erfahrenen Entwicklern freut sich darauf, Ihr Projekt zu besprechen und Ihnen dabei zu helfen.
Werfen Sie einen Blick auf unsere Entwicklungsleistungen
Was ist ein Handler?
Damit MAUI als plattformübergreifendes UI-Framework funktionieren kann, muss es wissen, welche nativen Steuerelemente je nach Plattform verwendet werden sollen, wenn jemand z. B. eine Schaltfläche oder ein Etikett in der Benutzeroberfläche definiert. Wenn zum Beispiel jemand einen Button in .NET MAUI definiert, entscheidet der plattformspezifische Handler, einen UIButton auf iOS zu verwenden.
Dem Diagramm aus der offiziellen Dokumentation folgend, haben wir unsere Zielarchitektur unten dargestellt. Um einen schnelleren Start zu ermöglichen, haben wir uns entschieden, den Code für die Kartenansicht aus der Xamarin Forms-Implementierung zu kopieren und eine entsprechende IMap-Schnittstelle zu erstellen. Sie finden die Implementierung für die Map-Klasse im Xamarin Forms GitHub Repository. Sie ist auch eine Inspiration für fortgeschrittenere Themen wie die Anzeige von Pins oder das Setzen einer bestimmten Position auf der Karte. Mit der Veröffentlichung von .NET MAUI für .NET 7 müssen Sie diese Dateien nicht mehr kopieren, da sie jetzt zusammen mit den Handlern Teil des .NET MAUI-Frameworks sind.

Ein Handler in .NET MAUI wird auch verwendet, um Funktionen zu bestehenden Steuerelementen im Framework hinzuzufügen oder um sie mit vollständig benutzerdefinierten Implementierungen zu erweitern. Wenn Sie bereits mit dem Prinzip der benutzerdefinierten Renderer von Xamarin Forms vertraut sind, wird der Wechsel zu Handlern in MAUI nicht schwierig sein.
Erstellen des BaseHandlers
Um .NET MAUI mit einem neuen Map Control zu erweitern, benötigen Sie einen BaseHandler für den plattformunabhängigen Teil und die Implementierungen für jede zu unterstützende Plattform. In unserem Fall Android und iOS. Der BaseHandler besteht im Wesentlichen nur aus den Methoden und Eigenschaften, die unser Map-Handler zur Verwendung bereitstellen will.
public partial class MapHandler
{
public static IPropertyMapper<IMap, MapHandler> MapMapper = new PropertyMapper<IMap, MapHandler>(ViewMapper)
{ };
public MapHandler() : base(MapMapper)
{ }
}
Es ist wichtig zu erwähnen, dass wir nicht gegen das konkrete Map Control arbeiten, sondern gegen die IMap-Schnittstelle. Unsere plattformspezifischen Handler-Implementierungen werden von der generischen ViewHandler-Implementierung abgeleitet. Damit müssen drei wichtige Methoden implementiert werden:
- CreatePlatformView wird aufgerufen, um die native Ansicht zu instanziieren und einzurichten.
- ConnectHandler wird jedes Mal aufgerufen, wenn unser benutzerdefiniertes Steuerelement auf einer Seite platziert wird, die angezeigt werden soll.
- DisconnectHandler wird jedes Mal aufgerufen, wenn die Seite, auf der sich unser benutzerdefiniertes Steuerelement befindet, verschwindet.
Erstellen des Apple Maps Handlers
Aufgrund der Tatsache, wie die Apple Maps API aufgebaut ist, ist es einfacher, mit dem Schreiben eines MAUI Maps Handlers zu beginnen. Wir müssen eine iOS-MapHandler-Klasse erstellen, die auch eine partielle Klasse mit demselben Namen ist, aber im plattformspezifischen Ordner abgelegt ist. Wie bereits erwähnt, haben wir von der generischen ViewHandler-Implementierung abgeleitet, in der wir unser Karten-Steuerelement mit dem iOS-spezifischen MKMapView-Steuerelement verbinden. Ein vereinfachter Apple Maps MAUI-Handler kann wie die folgende Implementierung aussehen.
public partial class MapHandler : ViewHandler<Map, MKMapView>
{
public MapHandler(IPropertyMapper mapper, CommandMapper commandMapper = null)
: base(mapper, commandMapper)
{ }
protected override MKMapView CreatePlatformView()
{
return new MKMapView(CoreGraphics.CGRect.Empty);
}
protected override void ConnectHandler(MKMapView PlatformView)
{ }
protected override void DisconnectHandler(MKMapView PlatformView)
{
// Clean-up the native view to reduce memory leaks and memory usage
if (PlatformView.Delegate != null)
{
PlatformView.Delegate.Dispose();
PlatformView.Delegate = null;
}
PlatformView.RemoveFromSuperview();
}
}
Mac und iOS teilen sich die zugrunde liegenden Frameworks und APIs für die Handhabung der Benutzeroberfläche. Auf diese Weise erhalten wir durch Kopieren des Handlers in den MacCatalyst-Ordner des Projekts auch diese Plattform unterstützt.
Erstellen des Google Maps Handlers
Damit Google Maps in unserer .NET MAUI-Anwendung funktioniert, müssen wir einige Vorarbeiten leisten.
Zunächst müssen wir das NuGet-Paket Xamarin.GooglePlayServices.Maps referenzieren. Dann müssen verschiedene Einträge in AndroidManifest.xml vorgenommen werden, damit Google Maps einen API-Schlüssel verwenden kann. Der beste Weg, dies zu tun, ist, die Xamarin Google Maps-Dokumentation zu lesen.
Werfen wir einen Blick auf den .NET MAUI Android Handler. Damit wir mit dem Google Maps-Steuerelement interagieren können, müssen wir auf den OnMapReady-Callback warten. Leider stoßen wir auf eine InvalidCastException, wenn wir versuchen, die IOnMapReady-Schnittstelle direkt in unserem ViewHandler zu implementieren und zu verwenden. Die Lösung für dieses Problem ist unsere MapHelper-Klasse. Sie wird von Java.Lang.Object abgeleitet und macht es uns daher leicht, die IOnMapReady-Schnittstelle zu implementieren.
public partial class MapHandler : ViewHandler<MapView, Android.Gms.Maps.MapView>
{
private MapHelper _mapHelper;
internal static Bundle Bundle { get; set; }
public MapHandler(IPropertyMapper mapper, CommandMapper commandMapper = null)
: base(mapper, commandMapper)
{ }
protected override Android.Gms.Maps.MapView CreatePlatformView()
{
return new Android.Gms.Maps.MapView(Context);
}
protected override void ConnectHandler(Android.Gms.Maps.MapView platformView)
{
base.ConnectHandler(platformView);
_mapHelper = new MapHelper(Bundle, platformView);
_mapHelper.MapIsReady += _mapHelper_MapIsReady;
_mapHelper.CallCreateMap();
}
private void _mapHelper_MapIsReady(object sender, EventArgs e)
{
_mapHelper.Map.UiSettings.ZoomControlsEnabled = true;
_mapHelper.Map.UiSettings.CompassEnabled = true;
}
}
class MapHelper : Java.Lang.Object, IOnMapReadyCallback
{
private Bundle _bundle;
private Android.Gms.Maps.MapView _mapView;
public event EventHandler MapIsReady;
public GoogleMap Map { get; set; }
public MapHelper(Bundle bundle, Android.Gms.Maps.MapView mapView)
{
_bundle = bundle;
_mapView = mapView;
}
public void CallCreateMap()
{
_mapView.OnCreate(_bundle);
_mapView.OnResume();
_mapView.GetMapAsync(this);
}
public void OnMapReady(GoogleMap googleMap)
{
Map = googleMap;
MapIsReady?.Invoke(this, EventArgs.Empty);
}
}
Zu guter Letzt müssen wir das Android OnCreate Lifecycle Event an unsere MapView übergeben. Dazu benötigen wir ein Android Bundle.
Alles ist bereits im vorherigen Code enthalten. Es gibt eine statische Bundle-Eigenschaft, die wir nun setzen müssen. Dazu wechseln wir in die MainActivity des Android-Projekts und passen den Code entsprechend an.
public class MainActivity : MauiAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
MapHandler.Bundle = savedInstanceState;
}
}
Wenn Sie eine Meldung sehen wie: Warnung: Warnung XA4212: Typ MapControlDemo.Handlers.MapHandler
implementiert Android.Runtime.IJavaObject
, erbt aber nicht Java.Lang.Object
oder Java.Lang.Throwable
. Dies wird nicht unterstützt. (MapControlDemo)
Das Hinzufügen des folgenden PropertyGroup-Eintrags in Ihr csproj sollte dies beheben:
<PropertyGroup Condition="$(TargetFramework.Contains('-android'))">
<AndroidErrorOnCustomJavaObject>false</AndroidErrorOnCustomJavaObject>
</PropertyGroup>
Da Sie es bis hierher geschafft haben, gehen wir davon aus, dass Sie ein gewisses Interesse an unserer Arbeit gewonnen haben. Das ist völlig in Ordnung. Seien Sie unser Gast und geben Sie uns ein Feedback oder sprechen wir über Ihr Projekt. Wir freuen uns, von Ihnen zu hören.
Reden wir
Registrieren des Map-Handlern
Damit unsere .NET MAUI-Anwendung den Map-Handler verwenden kann, muss er noch registriert werden. Gehen Sie dazu in die Datei MauiProgram.cs und fügen Sie die folgenden Zeilen in den Builder ein:
.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler(typeof(MapHandlerDemo.Maps.Map),typeof(MapHandler));
})
Diese Zeile teilt MAUI mit, dass jedes Mal, wenn ein Aufruf von MapHandlerDemo.Maps.Map erfolgt, MAUI den MapHandler zum Rendern und Anzeigen dieses Steuerelements verwenden soll. Wenn der Handler nicht korrekt registriert wurde, wird jedes Mal, wenn Sie versuchen, eine Instanz des Handlers zu verwenden, die folgende Fehlermeldung ausgelöst: „System.Exception are thrown: Handler not found for view...“.
Nun ist es an der Zeit, sich die Karte anzusehen. Der Einfachheit halber habe ich eine neue ContentPage für unsere Karte direkt im Haupteinstiegspunkt erstellt und sie als Hauptseite festgelegt.
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MapsPage();
}
}
public class MapsPage : ContentPage
{
public MapsPage()
{
Content = new Maps.Map();
}
}
Je nach gewählter Plattform entspricht das Ergebnis der unten stehenden Grafik.

So geht's weiter
Wir haben eine Karte in unsere .NET MAUI-App integriert. Sie können nun die Funktionalität und das Verhalten nach Ihren Wünschen anpassen und erweitern. Auch der komplette Austausch des Kartenanbieters, z.B. statt Google Maps einfach Open Street Maps verwenden, ist nun möglich.
Fazit
Mit der neuen Projektstruktur und dem Handler-Konzept von .NET MAUI können plattformspezifische Steuerelemente recht schnell und einfach integriert werden. Ob Sie partielle Klassen mögen oder nicht, bleibt Ihnen überlassen. Was mir aber auf jeden Fall gefällt, ist der saubere und strukturierte Aufbau der Handler. Das Demoprojekt und alle Dateien sind auf dem öffentlichen Cayas Bitbucket zu finden.