アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

WPF で Google Map その 4

WPF で Google Maps API を利用するシリーズその 4。マーカーを利用できるとマップから位置を選択できるので、色々な機能に応用できそうだ。というわけで今回はマーカー操作を試す。

サンプル プログラム

前回サンプルからの変更点は以下。

  • マーカーの追加・削除、マーカー位置へのマップ移動機能を追加
  • マーカーの座標表示機能を追加

サンプル プログラムのプロジェクト一式は以下。ビルドには Visual Studio 2008 SP1、プログラムの実行には NET Framework 3.5 SP1 とネットワーク環境 (Google Maps API を使うので当たり前か...) が必要となる。

マーカーの操作

今回の実装を追加した事により map.js の内容が 100 行を越えたので、JavaScript のコードは重要な部分の抜粋に留める。

var marker = null; //! マーカー。
var map;           //! マップ オブジェクト。

/**
 * マップにマーカーを追加します。
 * 既にマーカーが存在する場合は何もしません。
 * マーカーの初期位置は、追加時点のマップ中央の座標となります。
 */
function addMarker()
{
    window.external.IsExistMarker = true;
    if( marker != null ) { return; }

    var center = map.get_center();
    marker = new google.maps.Marker( { position: center, map: map, title: "印", draggable: true });
    notifyMarkerLocation( center );

    // マップの中央位置が更新された時のイベント
    google.maps.event.addListener( marker, 'drag', function()
    {
        notifyMarkerLocation( marker.position );
    });
}

/**
 * マップからマーカーを取り除きます。
 * マーカーが存在しない場合は何もしません。
 */
function removeMarker()
{
    window.external.IsExistMarker = false;
    if( marker == null ) { return; }

    marker.setMap();
    marker = null;
    notifyMarkerLocation( null );
}

/**
 * マーカーの緯度・経度をマネージコードへ通知します。
 *
 * @param    latLng    マーカー座標。
 */
function notifyMarkerLocation( latLng )
{
    window.external.MarkerLocation = ( latLng == null ? "" : "緯度 = " + latLng.lat() + "、経度 = " + latLng.lng() );
}

/**
 * マーカーの座標へマップを移動します。
 */
function moveMapToMarker()
{
    if( marker == null ) { return; }

    map.setCenter( marker.position );
    notifyLocation();
}

addMarker でマーカー生成と初期設定を実行。

マーカーの draggable を true にするとドラッグ移動がサポートされる。drag イベントをハンドリングすることで操作ごとにマーカー座標をマネージ コード側へ通知している。マーカー移動はマップに比べると軽いので更新通知は遅延させず、そのまま処理している。

removeMarker はマーカーを削除する。Google Maps API としてマーカー削除機能は用意されておらず、マーカー インスタンスの setMap メソッドを引数なしで実行すると消える。Google のリファレンスによれば setMap を呼び出した後にインスタンスを null にするべしとあるので、そうしている。

window.external.IsExistMarker については後述。notifyMarkerLocation と moveMapToMarker は特別なことをしていないので解説を省く。

WPF のボタン部分の更新

今回のプログラムでは、JavaScript 側でマーカーの追加・削除が行われた時、WPF 部分の「マーカーの追加・削除」と「マーカーへの移動」ボタンの状態を更新されるようにしている。

これを実現する為の window.external に関連付けられるマネージ クラスの実装は以下のようになる。

/// <summary>
/// Google Map の操作を行う JavaScript に関連付けられるクラスです。
/// </summary>
[ComVisible( true )]
public class MapHost : INotifyPropertyChanged
{
    /// <summary>
    /// マップ上にマーカーが配置されている事を示す値を取得または設定します。
    /// </summary>
    public bool IsExistMarker
    {
        get
        {
            return this._isExistMarker;
        }
        set
        {
            this._isExistMarker = value;
            this.NotifyPropertyChanged( "IsExistMarker", "MarkerCommandText" );
        }
    }

    /// <summary>
    /// マーカーへ実行可能な操作を示す文字列を取得します。
    /// </summary>
    public string MarkerCommandText
    {
        get { return this._isExistMarker ? "マーカー削除" : "マーカー追加"; }
    }

    /// <summary>
    /// マップ上にマーカーが配置されている事を示す値。
    /// </summary>
    private bool _isExistMarker;

    #region INotifyPropertyChanged メンバ

    /// <summary>
    /// プロパティが変更された事を通知するイベント ハンドラ。
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// プロパティの変更を通知します。
    /// </summary>
    /// <param name="names">変更されたプロパティ名のコレクション。</param>
    protected void NotifyPropertyChanged( params string[] names )
    {
        if( this.PropertyChanged != null )
        {
            foreach( string name in names )
            {
                this.PropertyChanged( this, new PropertyChangedEventArgs( name ) );
            }
        }
    }

    #endregion
}

JavaScript 側で window.external.IsExistMarker に値を設定すると MapHost.IsExistMarker が呼び出され、IsExistMarker と MarkerCommandText プロパティの更新が通知される。

「マーカーの追加・削除」ボタンの Content と「マーカーへの移動」ボタンの IsEnabled は XAML 上でこれらのプロパティにバインドされているため、この処理により JavaScript からの更新と連動するようになっている。

プログラムの実行

プログラムを実行すると、以下のようになる。

スクリーン ショット

初期状態ではマーカーなし。「マーカーの追加」ボタンを押す事でマップ中央に追加されてゆく。このボタンはマーカーが存在していると「マーカーの削除」という表示に変わるので、この時に押せばマーカーが消える。

マーカーをドラッグ操作すると、ウィンドウ下部のマーカー座標がリアルタイムに更新されてゆく。

マップ全体を移動させてから「マーカーへの移動」ボタンを押すと、マーカーが中央に表示されるようにマップ全体が位置調整される。

Comments from WordPress

  • 寺西弘明 寺西弘明 2016-11-14T00:29:08Z

    はじめまして。

    寺西と申します。

    古い記事について、ご質問させていただき恐縮ですが、WPF の WebBrowser コントロールで行えることは、iOS アプリの UIWebViewと同じでしょうか?

    それとも、制限や違いがありますでしょうか?

    お手数ですが、ご教授いただけますでしょうか。

    よろしくお願いいたします。

  • アカベコ アカベコ 2016-11-14T11:45:45Z

    ネイティブ層と Web ブラウザ内の JavaScript で連携可能な点については「同じ」ですが、連携方法は異なります。

    また WebBrowser コントロールはシステムにインストールされている IE を wrap していると思われますので、Web ブラウザとして見た場合、WebKit ベースの UIWebView に比べて HTML5/CSS3/ESNext 対応に不足がありそうです。

    c# - Add new Microsoft Edge to web browser control? - Stack Overflow
    http://stackoverflow.com/questions/31773359/add-new-microsoft-edge-to-web-browser-control

    この投稿を読むとサードパーティ製アドオンを使わない限り IE となるそうです。また IE のバージョンも最新ではなく 7 時点で止まっており、これを 11 にするにはレジストリを編集する必要があるらしく、厄介ですね。

    WebBrowserコントロールのIEバージョン - (。・ω・。)ノ・☆':;':
    http://www2.hatenadiary.jp/entry/2013/12/20/114342

    この制限を受け入れられず、かつ Web フロントエンドの技術を利用したいなら WPF ではなくなってしまいますが Electron でアプリ開発するほうがクロスプラットフォーム対応もできてよいと思います。