Friday, 29 July 2022

Display a map with .NET MAUI

Many apps, whether mobile or desktop, require the ability to show a map. However, .NET MAUI doesn’t currently have a view (control) capable of displaying a map. That’s frustrating because the underlying platforms that .NET MAUI supports all largely have native map views. Android has the MapView. iOS/MacCatalyst has MKMapView. WinUI has…nothing.

So, most of the platforms .NET MAUI runs on can display a map, but .NET MAUI itself lacks a cross-platform view to display a map. Therefore I’ve created a very simple cross-platform Map view. It was written primarily for me to understand how to write handlers, rather than being a fully featured control. It displays a map, lets you scroll it, zoom it, show your location, show traffic data, and change the map imagery (street/satellite/hybrid). There’s plenty it doesn’t do - no initialising the map to a specific location, no map pins, no routes, no drawing on the map surface etc. A fully featured Map view is beyond the scope of what I was attempting to do, and besides, there will be one appearing in .NET MAUI in the not too distant future.

As it’s a simpler example than the Video view I published yesterday, I thought I’d share it.

Handler architecture

.NET MAUI has an extension mechanism, known as handlers, that you can use to customise existing .NET MAUI controls, and write your own cross-platform views whose implementations are provided by native views.

Each .NET MAUI view has an interface representation, that abstracts a cross-platform view. Cross-platform views that implement these interfaces are known as virtual views. Handlers map these virtual views to native views on each platform, and are responsible for creating the underlying native view, and mapping their API to the cross-platform control.

Handlers are accessed through their view-specific interface. This avoids the cross-platform view having to reference its handler, and the handler having to reference the cross-platform view. Each handler typically provides a property mapper, and sometimes a command mapper, that maps the cross-platform view API to the native view API.

The following diagram shows the handler architecture for the Map view:

The Map view implements the IMap interface. On iOS/MacCatalyst, the MapHandler class maps the cross-platform Map view to an iOS/MacCatalyst MKMapView. On Android, the Map view is mapped to a MapView that's provided by the Xamarin.GooglePlayServices.Maps NuGet package. There’s no Windows implementation, due to the lack of a map control on WinUI.

The PropertyMapper in the MapHandler class maps the cross-platform view properties to native view APIs via mapper methods. Each platform then provides implementations of the mapper methods, which manipulate the native view API as appropriate. The overall effect is that when a property is set on the cross-platform view, the underlying native view is updated as required.

Handler implementations on each platform must override the CreatePlatformView method, and optionally the ConnectHandler and DisconnectHandler methods. The CreatePlatformView method should return the native view that implements the cross-platform view. The ConnectHandler method should perform any required native view setup, and the DisconnectHandler method should perform any required native view cleanup. Note that the DisconnectHandler override is intentionally not invoked by .NET MAUI - you have to invoke it yourself from a suitable place in your app’s lifecycle.

Code

I’m not going to provide a walkthrough of the code. But you can download it, and step through it yourself, by cloning the repo. However, I will give you some pointers to working through the code.

The important files in the solution are highlighted below:

  • Controls - the cross-platform view implementation. IMap abstracts the Map view and exposes members that the handler needs to be able to access, and derives from .NET MAUI’s View. The Map class, which derives from .NET MAUI’s View class, provides the cross-platform implementation, and is simply a collection of BindableProperty objects.
  • Handlers - The IMapHandler interface, which derives from .NET MAUI’s IViewHandler, specifies VirtualView and PlatformView properties. The VirtualViewproperty is used to access the cross-platform view from the handler, and the PlatformView property is used to access the native view that implements the Map view. The MapHandler class is a partial class, whose platform-specific implementations are in the MapHandler.Android.cs, MapHandler.iOS.cs and MapHandler.Windows.cs files.

A handler must be registered against its cross-platform view, and this takes place in MauiProgram.cs with the ConfigureMauiHandler/AddHandler methods.

On Android you’ll need to insert your Google Map API key into the Android Manifest for the map to appear.

I hope this ends up being useful to folks on their journey to understand how to write custom controls backed by .NET MAUI handlers.

No comments:

Post a Comment