泥庭

2015年1月16日

WPFでBingMapを使う(その2)

Filed under: C#, WPF — タグ: , — yone64 @ 9:03 PM
前回の続きです。
本編の前に、一つ訂正。
「Bing Map WPF Controlは、NuGetで提供されてないのでとっとと対応すべき。」みたいなことを書きましたが、どうやら提供されていた(過去形)ようです。現在は提供されなくなっていますが、いったい何が。

実行時例外

いきなり身もふたもないタイトルですが、BingMapControlはとある条件で実行時例外を投げます。
例えば、こんな感じのXAMLを用意します。
<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>
        <ContentControl>
            <ContentControl.Template>
                <ControlTemplate>
                    <wpf:Map CredentialsProvider="ここにAPIキーを入力"/>
                </ControlTemplate>
            </ContentControl.Template>
        </ContentControl>
    </Grid>
</Window>
で、とりあえず実行してみます。すると、なんということでしょう。
System.Windows.Markup.XamlParseException はハンドルされませんでした。
HResult=-2146233087
Message=指定されたバインディング制約に一致する型 ‘Microsoft.Maps.MapControl.WPF.Map’ のコンストラクターの呼び出しで例外がスローされました。
Source=PresentationFramework
LineNumber=0
LinePosition=0
StackTrace:
場所 System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlReader templateReader, XamlObjectWriter currentWriter)
場所 System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlObjectWriter objectWriter)
場所 System.Windows.FrameworkTemplate.LoadOptimizedTemplateContent(DependencyObject container, IComponentConnector componentConnector, IStyleConnector styleConnector, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)
場所 System.Windows.FrameworkTemplate.LoadContent(DependencyObject container, List`1 affectedChildren)
場所 System.Windows.StyleHelper.ApplyTemplateContent(UncommonField`1 dataField, DependencyObject container, FrameworkElementFactory templateRoot, Int32 lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate)
場所 System.Windows.FrameworkTemplate.ApplyTemplateContent(UncommonField`1 templateDataField, FrameworkElement container)
場所 System.Windows.FrameworkElement.ApplyTemplate()
場所 System.Windows.FrameworkElement.MeasureCore(Size availableSize)
場所 System.Windows.UIElement.Measure(Size availableSize)
場所 System.Windows.Controls.Grid.MeasureOverride(Size constraint)
場所 System.Windows.FrameworkElement.MeasureCore(Size availableSize)
場所 System.Windows.UIElement.Measure(Size availableSize)
場所 MS.Internal.Helper.MeasureElementWithSingleChild(UIElement element, Size constraint)
場所 System.Windows.Controls.ContentPresenter.MeasureOverride(Size constraint)
場所 System.Windows.FrameworkElement.MeasureCore(Size availableSize)
場所 System.Windows.UIElement.Measure(Size availableSize)
場所 System.Windows.Documents.AdornerDecorator.MeasureOverride(Size constraint)
場所 System.Windows.FrameworkElement.MeasureCore(Size availableSize)
場所 System.Windows.UIElement.Measure(Size availableSize)
場所 System.Windows.Controls.Border.MeasureOverride(Size constraint)
場所 System.Windows.FrameworkElement.MeasureCore(Size availableSize)
場所 System.Windows.UIElement.Measure(Size availableSize)
場所 System.Windows.Window.MeasureOverrideHelper(Size constraint)
場所 System.Windows.Window.MeasureOverride(Size availableSize)
場所 System.Windows.FrameworkElement.MeasureCore(Size availableSize)
場所 System.Windows.UIElement.Measure(Size availableSize)
場所 System.Windows.Interop.HwndSource.SetLayoutSize()
場所 System.Windows.Interop.HwndSource.set_RootVisualInternal(Visual value)
場所 System.Windows.Interop.HwndSource.set_RootVisual(Visual value)
場所 System.Windows.Window.SetRootVisual()
場所 System.Windows.Window.SetRootVisualAndUpdateSTC()
場所 System.Windows.Window.SetupInitialState(Double requestedTop, Double requestedLeft, Double requestedWidth, Double requestedHeight)
場所 System.Windows.Window.CreateSourceWindow(Boolean duringShow)
場所 System.Windows.Window.CreateSourceWindowDuringShow()
場所 System.Windows.Window.SafeCreateWindowDuringShow()
場所 System.Windows.Window.ShowHelper(Object booleanBox)
場所 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
場所 MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
場所 System.Windows.Threading.DispatcherOperation.InvokeImpl()
場所 System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
場所 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
場所 System.Windows.Threading.DispatcherOperation.Invoke()
場所 System.Windows.Threading.Dispatcher.ProcessQueue()
場所 System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
場所 MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
場所 MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
場所 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
場所 MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
場所 System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
場所 MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
場所 MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
場所 System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
場所 System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
場所 System.Windows.Threading.Dispatcher.Run()
場所 System.Windows.Application.RunDispatcher(Object ignore)
場所 System.Windows.Application.RunInternal(Window window)
場所 System.Windows.Application.Run(Window window)
場所 System.Windows.Application.Run()
場所 BingMapSample.App.Main() 場所 c:\Users\******\Documents\Visual Studio 2013\Projects\BingMapSample\BingMapSample\obj\Debug\App.g.cs:行 0
場所 System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
場所 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
場所 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state)
場所 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
場所 System.Threading.ThreadHelper.ThreadStart()
InnerException: System.InvalidOperationException
HResult=-2146233079
Message=ディスパッチャーの処理の中断中は、この操作を実行できません。
Source=WindowsBase
StackTrace:
場所 System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
場所 System.Windows.Threading.DispatcherOperation.Wait(TimeSpan timeout)
場所 System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherOperation operation, CancellationToken cancellationToken, TimeSpan timeout)
場所 System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
場所 System.Windows.Threading.Dispatcher.Invoke(Delegate method, Object[] args)
場所 Microsoft.Maps.MapControl.WPF.Core.MapConfigurationFromWeb.ConfigLoaded(String requestKey, Dictionary`2 sections)
場所 Microsoft.Maps.MapControl.WPF.Core.MapConfigurationFromWeb.GetConfigurationSection(String version, String sectionName, String culture, MapConfigurationCallback callback, Boolean reExecuteCallback, Object userState)
場所 Microsoft.Maps.MapControl.WPF.Core.MapConfiguration.GetSection(String version, String sectionName, String culture, String key, MapConfigurationCallback callback, Boolean reExecuteCallback, Object userState)
場所 Microsoft.Maps.MapControl.WPF.Core.MapConfiguration.GetSection(String version, String sectionName, String culture, String key, MapConfigurationCallback callback, Boolean reExecuteCallback)
場所 Microsoft.Maps.MapControl.WPF.Map..ctor()
InnerException:
結構、えらい感じのStackTraceが吐かれてしまいますね。で、重要なのはこの部分「ディスパッチャーの処理の中断中は、この操作を実行できません。」です。
何故Dispatcherが中断中かは非常に謎ですが、Dispatcher外で初期化処理が呼び出されているようです。
Templateに含まれるコントロールの作成はDispatcher上で実行されているはずなのですが…。

回避

まぁ、そういうわけで原因は不明なのですが、エラーが起こっておりこれを解決しなければいけません。
日本語エラーメッセージをこの辺で検索して、英語でのエラーメッセージに変換し、さらに英語のエラーメッセージで検索すると比較的解決方法はすぐ見つかります。
わかりやすいのは、この辺でしょうか?
要するに、Dispatcher上で非同期実行できるようにBeginInvokeで囲んであげればよいよということですね。
なるほど、では早速適用を…。ちょっと待てよ、初期化処理自体がXAMLで書かれてるのに、BeginInvokeで囲むとかどうするのよ。

決着

XAML上で非同期実行とかできるわけがないので(出来たらゴメンね)、あとはコードで書くしかありません。
でも、TemplateはXAMLで書きたいですよね。あと、Templateの種類によってはコードでは完全には再現できません。
なので、UserControlでWrapしてみました。
/// <summary>
/// UserControl1.xaml の相互作用ロジック
/// </summary>
public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
        App.Current.Dispatcher.BeginInvoke(
            new Action(() =>
                this.Content =new Map
                {
                    CredentialsProvider =new ApplicationIdCredentialsProvider("ここにAPIキーを入力")
                }));
    }
}
<Window x:Class="BingMapSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:bingMapSample="clr-namespace:BingMapSample"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ContentControl>
            <ContentControl.Template>
                <ControlTemplate>
                    <bingMapSample:UserControl1/>
                </ControlTemplate>
            </ContentControl.Template>
        </ContentControl>
    </Grid>
</Window>
これで無事実行可能に、あとは必要に応じてMapコントロールの各プロパティとメソッドをUserControl側からバイパスしてあげればOKですね。
#なんだろう。解決したのに、この敗北感は。

コメントする »

まだコメントはありません。

RSS feed for comments on this post. TrackBack URI

コメントを残す

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

WordPress.com ロゴ

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

Twitter 画像

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

Facebook の写真

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

Google+ フォト

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

%s と連携中

WordPress.com Blog.

%d人のブロガーが「いいね」をつけました。