C#でFormにBingマップを表示する

# はじめに
Windows Formsに触れたことがある人なら、きっと誰もがマップをFormに埋め込みたいと思うでしょう。
しかしWindows Formsには、残念ながらマップを表示するコントロールはありません。
しかし、CefSharpとBing Maps APIを組み合わせればそのような機能を実現できます。
今回はそのやり方を紹介します。

# HTMLファイルを作る
仕組みとしては、Bing Mapsを埋め込んだHTMLファイルをCefSharpで表示する形となります。
本当はGoogle Maps APIを使いたかったのですが、クレジットカード情報を登録する必要があるみたいで面倒だったので、今回はあきらめました。

Bing Maps APIを使うにはMicrosoftのアカウントが必要です。持っていない方は登録しましょう。
持っている方は[Bing Maps APIのページ](https://www.bingmapsportal.com/)にアクセスし、「Sign in」ボタンを押してサインインします。
サインインできたら、上のメニューの「My account」から「My keys」をクリックします。
「Create key」という画面が表示されたら、必要事項を記入し「Create」をクリックします。
するとキーの一覧が表示されるので、「Show key」を押せばキーが表示されます。
このキーは後で使うのでコピーしておいてください。

それではHTMLを記述していきます。

```html

<!DOCTYPE html>
<html>
  <head>
    <title>Bing Maps API</title>
    <meta charset="utf-8" />
    <script type='text/javascript'
      src='http://www.bing.com/api/maps/mapcontrol?callback=GetMap&key=[キーを指定]&setLang=ja' 
      async defer></script>
    
   <script type='text/javascript'>
    var map;
    function GetMap() {
      map = mapStart(35, 140, 10);
    }

    // Mapを初期化
    // 緯度, 経度, 拡大率(1～20)
    function mapStart(lat, lon, num) {
      return new Microsoft.Maps.Map('#myMap', {
        center: new Microsoft.Maps.Location(lat, lon),
        // 表示モード(road:普通, aerial:航空写真)
        mapTypeId: Microsoft.Maps.MapTypeId.aerial,
        zoom: num
      });
    }

    // ピンを立てる(緯度, 経度)
    function mapPushpin(lat, lon) {
      const location = new Microsoft.Maps.Location(lat, lon);
      const pin = new Microsoft.Maps.Pushpin(location, {
        color: "red",     // ピンの色
        draggable: true,  // ドラッグできるか
      });
      map.entities.push(pin);
    }
    </script>
</head>
<body style="margin:0;">
    <div id="myMap" style="position:relative;width:100%;height:100%;"></div>
</body>
</html>
```

[キーを指定]のところに先ほど取得したキーを指定してください。
これをC:\Maps.htmlとして保存します。

# CefSharpの導入
次に、Bingマップを表示するアプリのプロジェクトを作成しておきましょう。
作成したら、このプロジェクトに「CefSharp」というパッケージを導入します。
CefSharpはChromiumのC#向け実装です。

「HTMLファイルを表示したいならWebBrowserコントロールを使えばいいのでは？」
と思うかもしれませんが、WebBrowserコントロールは内部に古いIEのエンジンを使っているので、エラーが頻発してまともに使えません。

今回は、NuGetでCefSharp.WinFormsをインストールします。
![スクリーンショット (52).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624097/d72f7cb5-3d6e-f876-0299-c49c4b74f0f6.png)

インストールが完了したら、プロジェクトのフォルダにある設定ファイル(csprojファイル)をテキストエディタで開きます。
以下のように、&lt;CefSharpAnyCpuSupport&gt;true&lt;/CefSharpAnyCpuSupport&gt;をPropertyGroupタグの中に追加して保存します。
![スクリーンショット (55).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624097/bef61fb1-9a4e-4fbe-93f4-cadb5bcf3bd8.png)

次に、App.configファイルを開き、configurationタグの中に

```xml

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <probing privatePath="x86"/>
    </assemblyBinding>
</runtime>
```

を追加して保存します。
![スクリーンショット (53).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624097/e536d03e-9ced-665c-316a-f7894539dbc9.png)

これでCefSharpの導入は完了です。

# Bingマップを表示するプログラム
それでは、プログラムを作っていきます。
以下のようにPanelやステータスバーなどを置きます。
CefSharpはツールボックスに表示できないので、Panelの内部に合わせて表示するようにします。
![スクリーンショット (54).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624097/bc7c0d99-8414-2acc-4e5e-efde5afff84b.png)

以下が今回のプログラムです。

```c#

using System;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;

namespace BingMapsSample
{
    public partial class Form1 : Form
    {
        string maphtml = @"C:\Maps.html";  // さっきのHTMLファイル
        ChromiumWebBrowser cefBrowser;
        CefSettings settings;
        public Form1()
        {
            InitializeComponent();
            button1.Enabled = false;
            toolStripStatusLabel1.Text = "読み込み中...";

            settings = new CefSettings();
            // レンダリングを最適化(これをやらないとバグる)
            settings.SetOffScreenRenderingBestPerformanceArgs();
            Cef.Initialize(settings);
            cefBrowser = new ChromiumWebBrowser(maphtml);
            cefBrowser.LoadingStateChanged += CefBrowser_LoadingStateChanged;
            // Panelに合わせて表示
            panel1.Controls.Add(cefBrowser);
            cefBrowser.Dock = DockStyle.Fill;
        }

        private void CefBrowser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e)
        {
            if(!e.IsLoading)
            {
                // 読み込み完了時
                Invoke((MethodInvoker)delegate {
                    toolStripStatusLabel1.Text = "読み込み完了";
                    button1.Enabled = true;
                });
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Javascriptの実行
            cefBrowser.ExecuteScriptAsync("map = mapStart(" + textBox1.Text +
                "," + textBox2.Text + "," + "10);");
            cefBrowser.ExecuteScriptAsync("mapPushpin(" + textBox1.Text +
                "," + textBox2.Text + ");");
        }
    }
}
```

button1_Click内ではExecuteScriptAsyncメソッドを使って、HTMLファイルで定義した関数を呼び出しています。
実行するとこのようにマップが表示されます。
![スクリーンショット (56).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624097/70045830-be8f-0b17-861f-94ac57441c96.png)
緯度と経度を入力して「移動」を押せば
![スクリーンショット (57).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624097/34df7e1f-5ee4-d675-a889-3d2445c829a0.png)
このようにマークが付きます。

# おわりに
以上、C#でFormにBingマップを表示する方法でした。
回りくどい方法でしたが、意外とちゃんと動くので是非試してみてください。

# 参考文献
* [BingMapsを簡単に使う関数を作る](https://qiita.com/daisu_yamazaki/items/21cf0d7ca8b4b64847d5)
* [WinFormsでCefSharpをAnyCPU構成で使う方法](https://takumi-oda.com/blog/2019/12/09/post-1017/)
* [Creating a Basic Map Control](https://docs.microsoft.com/en-us/bingmaps/v8-web-control/creating-and-hosting-map-controls/creating-a-basic-map-control)
* [CefSharp screen rendering problems Windows 10 touch screen](https://stackoverflow.com/questions/38387740/cefsharp-screen-rendering-problems-windows-10-touch-screen)
* [MapTypeId Enumeration](https://docs.microsoft.com/en-us/bingmaps/v8-web-control/map-control-api/maptypeid-enumeration)
* [Embed Google Maps In .NET Desktop Application](https://www.c-sharpcorner.com/article/embed-google-maps-in-net-desktop-application/)