GeoDiveExaのメイン機能である地図の移植は、CustomPinや背景地図、デジタルズーム機能が1番の問題です。まずはCustomPinの移植を行いました。

1)Mapの移植
GeoDiveExaはMark位置を表すピンやターゲットを表すピンなど色々なタイプのピンを使用しており、背景地図も使用して居たのでXamarinの場合はCustomMapを作成しCustomRanderでiOS部分の実装を行なって居ましたがNetMAUIではCustomHandlerのみで大体の実装は出来そうです。開発環境のフォルダー構造を見ても実際のファイルを見ても随分スッキリしており、iOS部分も少なくなって居ますので、Androidへの移植もXamarinよりNetMAUIが簡単の様に見えます。
1−1)関連ファイルとクラス対比
Map関連ファイル対応表。一番大きな違いはCustomMapRendererがCustomMapHandlerになる事です。またCustomMapHandlerを使う為に、MauiProgram.csにMapとCustomMapHandlerを使用する事を宣言して居ます。
XamarinのMap関連部分 | NetMAUIのMap関連部分 |
---|---|
<CustomMap.cs> CustomPin 実装 CustomMAP 実装 | <CustomPin.cs> CustomPin 実装 |
<CustomMapRenderer.cs> CustomMKAnnotationView 実装 CustomTileOverlay 実装 CustomMapRenderer 実装 | <CustomMapClass.cs> CustomAnnotation 実装 CustomTileOverlay 実装 CustomMapHandler 実装 |
なし | <MauiProgram.cs> .UseMauiMaps() 実行 .ConfigureMauiHandlers(handlers => { handlers.AddHandler <Microsoft.Maui.Controls.Maps.Map, CustomMapHandler>(); }); 実行 |
2)CustomPinの移植(1)
XamarinのCustomPinをNetMAUIに移植するには、CustomRendererをCustomHandlerにして。。。と書いてありますが、今一つピンときません。なのでGeminiに聞いてみます。
<geminiの回答例>
Xamarin.Formsから.NET MAUIに移行されたのですね。.NET MAUIでは、より柔軟にカスタムピンを作成できます。以下に、.NET MAUIでCustomPinを実装する際のサンプルコードと解説をいくつかご紹介します。
C#
public class CustomPin : Pin
{
public CustomPin(string label, double latitude, double longitude, string pinImage)
{
Label = label;
Position = new Position(latitude, longitude);
IconImageSource = pinImage;
}
}
[assembly: ExportRenderer(typeof(CustomPin), typeof(CustomPinRenderer))]
namespace YourNamespace.iOS
{
public class CustomPinRenderer : ViewRenderer<CustomPin, MKAnnotationView>
{
protected override void OnElementChanged(ElementChangedEventArgs<CustomPin> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var annotationView = (MKAnnotationView)Control;
annotationView.Image = UIImage.FromFile("custom_pin.png");
annotationView.CanShowCallout = true;
annotationView.Selected += (sender, args) =>
{
var customPin = (CustomPin)Element;
// ピンがタップされた時の処理
// 例: メッセージを表示
var alert = UIAlertController.Create("ピンタップ", $"ピンラベル: {customPin.Label}", UIAlertAction.Cancel);
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(alert, true, null);
};
}
}
}
}
これをそのまま実装してもエラーばかりです。回答のソースはXamarin時代の物も混じっている様な感じですね。PositionがあるしassemblyもRendererもあります。いくつか聞いてみても有用なコードサンプルは出てきませんので、通常のGoogle検索で参考となるサイトやサンプルを探しましたが、右往左往するばかりで、同じ様な人々が多く居ました。結局使えたのは以下のサイトのみでした(=有料のパッケージはいくつかありました)。
Customize map pins in .NET MAUI
CustomPinを作成するためのキー項目は以下の4項目でした。
キー項目 | 内容と感想 |
---|---|
1)カスタムマップハンドラーを登録する 例) var builder = MauiApp.CreateBuilder(); builder.UseMauiApp<App>().UseMauiMaps(); builder.ConfigureMauiHandlers(handlers => { handlers.AddHandler<Microsoft.Maui.Controls.Maps.Map, CustomMapHandler>(); }); | マップの動作イベントを変更してPinの画像を設定するので、そのハンドラーを設定しないと、CustomMapHandler動作自体ができない。この点が検索してもAIに聞いてもなかなか出てこないので、みんな苦労している点なのでしょう。 |
2)Pinにイメージ情報を付加する為にCustomPinを作成する 例) public class CustomPin : Pin { public static readonly BindableProperty ImageSourceProperty = BindableProperty.Create(nameof(ImageSource), typeof(ImageSource), typeof(CustomPin); | イメージ情報(ImageSource)を追加したCustomPinを作成するのは普通の事として納得いきます。 |
3)Map.PinsにCustomPinを追加すると、PinsのPropertyChangedイベントが自動的に実行されるで、それを受け取る為にPropertyMapper をCustomMapHandlerに追加する。例)例ではMap.Pinsの変更イベントで関数MapPinsを実行する様にしています。 public static readonly IPropertyMapper<IMap, IMapHandler> CustomMapper = new PropertyMapper<IMap, IMapHandler>(Mapper) { [nameof(IMap.Pins)] = MapPins, }; | ここも嵌るポイントですね。特に設定せずともMap.Pinsのイベントが実行されるなんて、どこに書いてあるのか?(=知った後に調べると確かにありましたが、PropertyChangedイベントが発生するとは書いてありません。。。が、常識なのですね)。 このイベントが最初からあるので、特別に作る必要もないのが、迷わせる点ですね。 後は、このイベントを横取りしてMapPinsを実行します。 |
4)PinsのPropertyChangedをCustomMapHandlerのMapPinsで判定して、イメージを設定する事によりカスタムピンを作成する。 | MapPinsで、map.Pinsのデータ(CustomPin)を取得して、後は自由にiOSのMap用のMKAnnotation を作成すればカスタムピンの出来上がりです。 |
この方法でCustomPinを作成してGeoDiveExaで使用しているPinの描画ができる様になりました。
3)CustomPinの移植(2)
CustomPinは一応できましたが、上記のフローではPins=List<CustomPin>の変更イベントを使用してカスタムピンを作成している為に、毎回全部のMKAnnotation
を消してから作成しているフローなので非効率でした。
GeoDiveExaの動作ではCustomPinの追加と、以前作成したCustomPinの交換のみです(=削除もありますが、これは通常動作で可能ですから考慮しません)。

Pinの追加と交換に使用できる様なプロパティーがIMapにないか???と調べましたが、使えそうな物が有りませんでした。
なので、「常識の独自のプロパティを作成して、PropertyChangedイベントを発生させて、CustomMapHandlerで横取り」する事にします(=これは、NetMAUIのお作法に則っている方法とは言えないと思いますが、もっと根源的な常識なので良しとしましょう)。
CustomPinの修正 | 内容 |
---|---|
1)MapViewModelにAddNewPinとReplacePinを作成する | PropertyChangedイベントを発生させる変数を作成します。 |
2)AddNewPinとReplacePinのsetにOnPropertyChangedを追加する。 例) private CustomPin _addNewPin; public CustomPin AddNewPin { get => _addNewPin; set { if (_addNewPin != value) { _addNewPin = value; OnPropertyChanged(); } } } | 変数に値を設定したらPropertyChangedイベント実行します。 |
3)CustomMapHandlerでAddNewPinとReplacePinのイベントを取得できるように、MapViewModelのPropertyChangedイベントをCustomMapHandlerのOnMapViewModelPropertyChangedに対応づける。 例) // CustomPin用 MapViewModel.PropertyChanged += OnMapViewModelPropertyChanged; | 常識のプロパティーチェンジイベントをとにかくCustomMapHandler内で処理できる様に設定します。 |
4)OnMapViewModelPropertyChangedでPropertyNameを比較して、AddNewPinならMapAddNewPinを実行する。 ReplacePinならMapReplacePinを実行する。 | これで1つのCustomPinの追加や、変更を自由に行えますので効率的です。 |
4)9月初めの移植地図画面
移動Pinも同じ様に表示される様になりました(=左:Xamarin版、右:NetMAUI版)。

コメントを残す