Windows 8 で解像度に対応すること 【横画面実装編】
#win8dev_jp
さて、Windows 8 で解像度に対応すること 【考え方編】 の続き。
基本の 1366x768 の縦横比を維持しながら拡大縮小する、ということは 1920x1080ではコンテンツの全てを縦横約x1.4するということになる。
これを勝手にやってくれるコントロールがこちらです。
ViewBox
ちなみに、Windows Phone 7.x には ViewBox はありませんでした。単一解像度(480x800)だったからそれほど困らなかったのです。そして、Windows Phone 8 になって解像度がふえました( 1280x768/720)。なので(かどうかわかりませんが) ViewBox が追加されました。
やってみよう
さて、どうするかというと、デフォルトでは Grid が置かれているのでそれをViewBoxで囲めばいい。
ただし、XAMLを直接書き換えないこと!グループ化からViewBoxを選びます。
まぁ、これでやると勝手に画面サイズを自動的に判断して入れ込んでくれるだけなんですけどね。
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
< /Grid>
↓
<Viewbox>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" Height="768" Width="1366" />
</Viewbox>
さて、まずは 1366x768 の元の状態
こちらはそのままGridに配置して表示したパターン。通常配置だと左上からの距離で配置されるのでこうなる。あちゃー。
そして ViewBox で囲んだパターン。Yes!これです。いいね。
16 : 9 の横画面に対応するならこれで終了
ただ、これでも万能ではないのも確か。縦横比を維持しながら拡大縮小だが、縦横比が異なる場合はどうしたらいいか?矛盾しているがとにかく回答を出さないといけない。
今のままで1024x768で表示するとこのようになる。これでよいのならばこれでいい。
対処方法は縦を長くすること。画面の縦横比に合わせてGridのサイズを変更する。つまり 1366x768 縦の幅 768 を変更するのである。
単純に画面の縦の幅に直すのではいけない。画面の縦横比のまま 1366x? になるように変更したい。
Grid の高さ= 1366 * 画面の高さ / 画面の幅
となる。1024x768 なら 1366x768/1024 ≒ 1025 となり、 Grid を 1366x768 から 1366x1025 に変更する。
動的処理はどこにいれる?
さて、この処理をどう実装するのか?
- 画面サイズの変更のイベントを取得し、MainPage.xaml.cs で実装
- Grid の縦サイズとPageの縦サイズをBindし、値の返還は Converter を作って適応する
どちらも1画面ならこれでいい。Converter のほうがもしかしたら、複数ページに対して楽に実装ができそうだ。でもいずれにせよ毎画面ごとに実装しないといけない。面倒でしょ?
どうする?
MainPage のクラスは何かできているか? Page クラスである。ならば Page クラスを改造してしまえばいい。Pageクラスを派生して、機能を実装してしまえばいい。
うーん、メニァック!
MyPage クラスを作る
なお、ここからの作業は理解のためにやるが、私の最終目的ではこの作業は消える運命にある。
新しい項目の追加でクラスを選択して MyPage クラスを作る。それから Page クラスを継承する。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
namespace App25
{
public class MyPage : Page
{
public MyPage()
{
Window.Current.SizeChanged += (s, e) =>
{
double w = e.Size.Width;
double h = e.Size.Height;
if (this.Content.GetType() == typeof(Viewbox))
{
Viewbox root = (Viewbox)this.Content;
UIElement viewBoxContent = ((Viewbox)this.Content).Child;
if (viewBoxContent.GetType() == typeof(Grid))
{
Grid grid = (Grid)viewBoxContent;
if ((w > 0) && (h > 0))
{
if (w > h)
{
grid.Width = 1366;
grid.Height = 1366 * h / w;
}
}
}
}
};
}
}
}
画面のサイズに合わせて計算して、それをViewBox直下のGridのサイズとして設定する。
その結果がこれ。レイアウトの処理はしないといけないが、ほぼ同じ大きさで縦横比もくるってはいないし上下のレターボックスも出ていない。
Visual Studio のテンプレートはカスタマイズページを派生しているからこれをもとにオレオレ空のプロジェクトを作る
さて、今回は自分でPageクラスを継承して作ったが、Visual Studio の グリッドアプリケーションや分割アプリケーションは Page クラスではなく、それを継承した LayoutAwarePage クラスを使っている。
ということで、ちょっとこれをもとにしたプレーンなプロジェクトを作ってみる。
いまさらながら、分割アプリケーションを元にプロジェクトを作ってみる。そして、新しい項目の追加で基本ページを追加する。空白のページは Page クラスを派生しているシンプルなページだから。名前を MainPage にして追加する。
起動時にMainPage を呼び出すように App.xaml.cs を書き換える。
if (!rootFrame.Navigate(typeof(MainPage), "AllGroups"))
{
throw new Exception("Failed to create initial page");
}
後は不要になった DataModel フォルダ、ItemsPage.xaml, SplitPage.xaml を削除する。あとは、VisualState Group 以外の画面要素は消してしまってもいい。
これでプレーンなプロジェクトができた。
あとはCommonフォルダの下の LayoutAwarePage.cs の WindowSizeChanged に追記する。
private void WindowSizeChanged(object sender, WindowSizeChangedEventArgs e)
{
double w = e.Size.Width;
double h = e.Size.Height;
if (this.Content.GetType() == typeof(Viewbox))
{
Viewbox root = (Viewbox)this.Content;
UIElement viewBoxContent = ((Viewbox)this.Content).Child;
if (viewBoxContent.GetType() == typeof(Grid))
{
Grid grid = (Grid)viewBoxContent;
if ((w > 0) && (h > 0))
{
if (w > h)
{
grid.Width = 1366;
grid.Height = 1366 * h / w;
}
}
}
}
this.InvalidateVisualState();
}
いっそ、ここにViewBox を入れ込む作業もコンストラクタに入れてしまってもいいが、XAML側で入れておいたほうが、デザイン時に確認ができるメリットがある。
ただ、まだ縦対応とスナップ対応がある。