かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Windows 8.1のストアアプリでマルチビューをサポートする方法のメモ

Windows 8.1 RTM + Visual Studio 2013 RC時点の情報です。

また、試し始めたばっかりで試行錯誤中のメモです。

新しいViewの作成

CoreApplicationのCreateNewViewでいけるっぽい。

var v = CoreApplication.CreateNewView();

これで、CoreApplicationViewというクラスのインスタンスがとれるので、いろいろできる。この段階でApplicationクラスのWindowCreatedイベントが発生しているので、新しいWindowが作られてるっぽい。

新しいViewに画面を表示する

CoreApplicationViewからDispatcherがとれるのでRunAsyncでフレーム作って画面遷移させればいい。

var v = CoreApplication.CreateNewView();
var viewId = default(int);
await v.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => 
{
    var f = new Frame();
    f.Navigate(typeof(BlankPage1));

    Window.Current.Content = f;
    ApplicationView.GetForCurrentView().Title = "Windowのタイトル";
});

新しいViewを画面に表示する

1つ前の見出しと紛らわしいですが、上のコードでは、まだ画面には出てこない状態です。画面に表示させるには、ApplicationViewSwitcher.TryShowAsStandaloneAsync(int viewId)を呼び出す必要があります。viewIdは、CoreApplicationViewのDispatcherの処理の中でApplicationView.GetForCurrentView()で取得できるApplicationViewのIdプロパティで取得できるものです。なので、こんな感じのコードに…

var v = CoreApplication.CreateNewView();
var viewId = default(int);
await v.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => 
{
    var f = new Frame();
    f.Navigate(typeof(BlankPage1));

    Window.Current.Content = f;
    var applicationView = ApplicationView.GetForCurrentView();
    applicationView.Title = "Windowのタイトル";
    viewId = applicationView.Id;
});
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(viewId);

このコードをボタンクリックのイベントとかに書くと、BlankPage1が表示されたウィンドウが画面に表示されます。

複数ウィンドウに一気に処理をしたりする

マルチビューの環境で、他のWindowに対して処理をするには、CoreApplicationViewをとっておけばできそうです。なので、以下のようなフィールドをどこかに定義してCoreApplicationViewをとっておくようにします。

List<CoreApplicationView> views = new List<CoreApplicationView>();

そして、新しいViewを作成するコードの中でリストに保持するようにします。

var v = CoreApplication.CreateNewView();
var viewId = default(int);
await v.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
    var f = new Frame();
    f.Navigate(typeof(BlankPage1));

    Window.Current.Content = f;
    var applicationView = ApplicationView.GetForCurrentView();
    applicationView.Title = "AppView: " + views.Count;
    viewId = applicationView.Id;
});
// とっとく
views.Add(v);
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(viewId);

あとは、CoreApplicationViewのDispatcherからRunAsyncすると、その中でWindow.Currentプロパティを使って、そのビューのWindowを取得できます。Windowが取得できたら後はお好きなように。

例えば、全てのビューのBlankPage1に対してSetというメソッドを呼ぶ場合は以下のようになります。

foreach (var v in this.views)
{
    var data = DateTime.Now.ToString();
    await v.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => 
    {
        var page = ((Windows.UI.Xaml.Controls.Frame)Window.Current.Content).Content as BlankPage1;
        page.Set(data);
    });
}

感想

ちゃんと作らないと、サスペンドからの復帰とか色々考えると頭が痛くなりそう。こんな風にCoreApplicationViewをリストで保持して、それを依り代にして操作をするのではなくて、どんなデータを元にしたビューを表示中なのかということをきちんとアプリケーションのロジック内で管理して、それを永続化しておく。そして、サスペンドからの復帰時には必要に応じて新しいビューを生成するといった形にしないと辛そう。

これは適当に作ると詰む系だと思いました。

あと、MSのサンプルだとCreateNewViewの後にWindowCreatedの完了を待って、WindowCreatedイベントハンドラの中でCoreDispatcerを取得するとかいう複雑怪奇なサンプルになってるんだけど、これは必須なのだろうか… Multiple Views Sample