泥庭

2015年1月14日

WPFでBingMapを使う

Filed under: .NET, C#, WPF — タグ: , , — yone64 @ 2:29 AM
今回からFont大き目でお届けしたいと思います。少し見やすくなるとよいのですが?

WPFで地図表示がしたい

地図表示といえば、やっぱりGoogleMapですかね。でも、WPF-C#からならBingMapが相性よさそうですよね。(ほら、なんとなくMicrosoft同士だし。)
と思って探しているとやっぱりありました。
Bing Maps WPF Control
しかし、いまどきmsiでインストールしなければいけないとかちょっと残念感が漂う感じ。(NuGetからインストールできるようになるとよいですね。)
というわけで、msiを落としてきてインストールすれば準備完了です。(あ、あとBingMapのAPI Keyも取得しておいてください)

地図表示アプリの作成

インストールが終了したら、さっそくプロジェクトを作成してみましょう。
# なお、ここからの作業はすべて、VisualStudio2013 & Windows8.1で行ってます。
  • 新規プロジェクト作成→参照設定追加
C:\Program Files (x86)\Bing Maps WPF Control\V1\Libraries にインストールされた、Microsoft.Maps.MapControl.WPF.dll への参照を追加します。
キャプチャ

  • コントロール追加
MainWindow.xamlにMapコントロールを追加します。

<Window x:Class="BingMapSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wpf="clr-namespace:Microsoft.Maps.MapControl.WPF;assembly=Microsoft.Maps.MapControl.WPF"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <wpf:Map/>
    </Grid>
</Window>
すると、デザインビューで地図が確認できます。
キャプチャ

  • 実行
ここで、一度実行してみましょう。
キャプチャ

地図が表示されましたね。「Invalid Credentials.~」というのはAPI Keyを入力してないがための文字列なのでとりあえずはOKです。

  • API Key設定
では、API Keyを設定して再び実行してみましょう

<wpf:Map CredentialsProvider="ここにAPI Keyを記述"/>
さて、再度実行!
キャプチャ

あれ?地図が表示されない!?

BingMapとの戦い(1回戦)

API Keyを設定したら、地図が表示されなくなってしまいました。仕方がないので、API Keyがない状態に戻して再度実行をしてみます。
やっぱり、地図は表示されません。なんだか不吉な感じがしてきました。ここで試したことを並べていきます。

  • リビルド → ×
  • ソリューションのクリーン → ×
  • Visual Studio再起動 → ×
  • Windows再起動 → ×
OSの再起動までしてダメなら、割ともう打つ手はない感じです。
仕方がないので、別のマシンを取り出して同じことを試してみます。そうこうしているうちに、ある共通点が見つかりました。

OSの1Userにつき、BingMapを表示するアプリケーションは一度目だけ地図が表示される。
これは、VisualStudioからの実行だけでなく、EXEを直接起動した場合も同じである
ということは、ここで一つの仮説が思いつきますね!

BingMapのDLLは、初回実行時にどこかにキャッシュ(?)を作成しており、キャッシュがある場合のみ地図表示に失敗する。(たぶんキャッシュロードのバグか何かで)
比較的善意に解釈してみました。(悪意を持って2回目以降の地図表示を拒否してるとどうしようもないけど、Microsoftのコントロールはそういうことしないよねという前提)
.NETのモジュールがキャッシュファイルを作りそうな箇所はそれほど多くないので探してみます。思いつくのは↓あたりでしょうか

  • C:\ProgramData
  • C:\Users\{各ユーザ}\AppData
で、結局下記にありました。
キャプチャ

IsolatedStorage(分離ストレージ)に保存されていたようです。
【参考】(@IT)分離ストレージを活用するには?[C#、VB]
ファイルを削除することにより、2回目以降の起動時にも地図が表示されることが確認されました。

対策検討会議

というわけで原因を見つけたものの、勝利!とはならないのです。
分離ストレージのファイルを削除すれば地図が表示されるとわかったものの、起動時に乱数で作ったっぽいフォルダ名の中にあるファイルを見つけて削除するというのは、まぁありえないわけです。
ならどうするか?

その前に、ひとつ疑問が。ヒントは2つ。

  • 2回目以降表示されないバグがあれば、さすがにネット検索にヒットしそう
  • 分離ストレージのファイル名末尾に「_JA-JP」とついている
ここから、導き出される答えは?
ひょっとして、日本語環境だけ?
日本語(+α)環境のみの不具合であれば、StackOverflowが引っかかることもないし、まぁありえる話かなと。
これは、英語OSでの実行を試してみるしかないですよね。でも英語OSなんて手元にない、なんてときは、Azureが便利ですよ。
で、結果はBingo。英語OSでは起きていなかったのです。

ここまでくれば、少し光明が見えてきた感じがします。
.NETのDLLが実行時のCultureを判断して何かしてるということは
  • Thread.CurrentThread.CurrentUICulture
  • Thread.CurrentThread.CurrentCulture
のどちらかが絡んでいるはず。(というか、英語OSを試す前に気付きたかったです。)
で、両方とも試した結果、CurrentUICultureが関連していることが判明。アプリケーションの起動時にCultureを変更するコードを入れた結果、無事地図が表示されるようになったのでした。
public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
    }
}
めでたし、めでたし。というわけにはいきませんね。CurrentUICultureがen-US固定になってしまいます。
さらに試行錯誤を加えた結果、次のことが判明しました。

Mapコントロールをアプリケーションで最初に初期化する時にen-USだったらよい

つまり、いったんCultureをen-USにして、Mapコントロールを初期化したのち、再びCultureを元に戻せばよさそうです。
結局、↓のようなコードを起動時に仕込むことになりました。

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var old = Thread.CurrentThread.CurrentUICulture;
        Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
        var dummy = new Map();
        Thread.CurrentThread.CurrentUICulture = old;
    }
}

余談

設定するCultureは、en-US以外にもen-GBでもよいようです。その他のCultureもいくつか試してみましたが、ダメでした。
この2つのCultureに共通する点として、一つ気付いたのは、距離の単位です。

キャプチャ

milesになっているのがわかると思います。ちなみに、公式にヤード・ポンド法が使われている国は、英国と米国だけだそうです。
ひょっとするとこのことが関係しているのかもしれません。(裏は取ってないので単なる想像ですが。)

後半へ続く

大きな落とし穴が開いていたBingMapControlですが、ひとまず表示できるようになりました。
ただ、戦いがこれで終わったわけではありません。というわけで、続きは次回。さらに大きな落とし穴が待ち受けています。(死
広告

1件のコメント »

  1. […] under: C#, WPF — タグ: BingMap, C# — yone64 @ 9:03 PM 前回の続きです。 本編の前に、一つ訂正。 「Bing Map WPF […]

    ピンバック by WPFでBingMapを使う(その2) | 泥庭 — 2015年1月16日 @ 9:03 PM


RSS feed for comments on this post. TrackBack URI

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

WordPress.com Blog.