コントロールの幅や高さを画面サイズのパーセントで指定するには?[ユニバーサルWindowsアプリ開発]:WinRT/Metro TIPS
コントロールがウィンドウサイズに対して一定の割合となるように設定したいことはよくある。本稿では、これを実現するための方法を二つ紹介する。
powered by Insider.NET
UIコントロールの幅や高さをウィンドウのサイズの一定割合にしたいと思ったことはないだろうか? 例えば、コントロールの幅をウィンドウ幅の半分にしたいといった場合だ。Gridコントロール(Windows.UI.Xaml.Controls名前空間)の比例分割でうまくいくことも多いが、うまくできないときはどうしたらよいだろうか? 本稿では、コードビハインドで制御する方法とカスタムビヘイビアーを利用する方法を紹介する。なお、本稿のサンプルは「Windows Store app samples:MetroTips #105」からダウンロードできる。
事前準備
ユニバーサルプロジェクトを使ってユニバーサルWindowsアプリを開発するには、以下の開発環境が必要である。本稿では、無償のVisual Studio Community 2013 with Update 4を使っている。
- SLAT対応のPC*1
- 2014年4月のアップデート*2適用済みの64bit版Windows 8.1 Pro版以上*3
- Visual Studio 2013 Update 2(またはそれ以降)*4を適用済みのVisual Studio 2013(以降、VS 2013)*5
*1 SLAT対応ハードウエアは、Windows Phone 8.1エミュレーターの実行に必要だ。ただし未対応でも、ソースコードのビルドと実機でのデバッグは可能だ。SLAT対応のチェック方法はMSDNブログの「Windows Phone SDK 8.0 ダウンロードポイント と Second Level Address Translation (SLAT) 対応PCかどうかを判定する方法」を参照。なお、SLAT対応ハードウエアであっても、VM上ではエミュレーターが動作しないことがあるのでご注意願いたい。
*2 事前には「Windows 8.1 Update 1」と呼ばれていたアップデート。スタート画面の右上に検索ボタンが(環境によっては電源ボタンも)表示されるようになるので、適用済みかどうかは簡単に見分けられる。ちなみに公式呼称は「the Windows RT 8.1, Windows 8.1, and Windows Server 2012 R2 update that is dated April, 2014」というようである。
*3 Windows Phone 8.1エミュレーターを使用しないのであれば、32bit版のWindows 8.1でもよい。
*4 マイクロソフトのダウンロードページから誰でも入手できる(このURLはUpdate 4のもの)。
*5 本稿に掲載したコードを試すだけなら、無償のExpressエディションやCommunityエディションで構わない。Visual Studio Express 2013 with Update 4 for Windows(製品版)はマイクロソフトのページから無償で入手できる。Expressエディションはターゲットプラットフォームごとに製品が分かれていて紛らわしいが、Windowsランタイムアプリの開発には「for Windows」を使う(「for Windows Desktop」はデスクトップで動作するアプリ用)。また、2014年11月12日(米国時間)に新しくリリースされたVisual Studio Community 2013 with Update 4(製品版)もマイクロソフトのページから無償で入手できる。Communityエディションは本稿執筆時点では英語版だけなので、同じ場所にあるVisual Studio 2013 Language Packの日本語版を追加インストールし、[オプション]ダイアログで言語を切り替える必要がある。
サンプルコードについて
Visual Studio 2013 Update 2(Update 3/4も)では、残念なことにVB用のユニバーサルプロジェクトのテンプレートは含まれていない*6。そのため、本稿で紹介するVBのコードはユニバーサルプロジェクトではなく、PCL(ポータブルクラスライブラリ)を使ったプロジェクトのものである。
*6 VB用のユニバーサルプロジェクトは、2015年の夏にリリースされるといわれているVisual Studio 2015(開発コード「Visual Studio 14」)からの提供となるようだ。プレビュー版で、すでに共有プロジェクトは利用可能になっている。「特集:次期Visual Studioの全貌を探る:Visual Basic 14の新機能ベスト10〜もう「VBだから」とは言わせない!」参照。
Gridコントロールを分割するときの問題点
例えば、画面幅いっぱいにGridコントロールを置き、その中で列を比例配分すれば、そのセルに入れたコントロールの幅はウィンドウのサイズに応じて変化することになる(次のコードと画像)。
<Grid ……省略……>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.6*"/>
<ColumnDefinition Width="0.4*"/>
</Grid.ColumnDefinitions>
<Image Source="Assets/hayashi_09.JPG" Stretch="UniformToFill" />
<TextBlock Grid.Column="1" TextWrapping="Wrap" FontSize="30">
……省略(長い文字列)……
</TextBlock>
</Grid>
この例では、Gridの中を左右に6対4の割合で分割している(太字の部分)。左側のセルに入れたImageコントロール(Windows.UI.Xaml.Controls名前空間)の幅は画面の60%になり、右側に入れたTextBlockコントロール(Windows.UI.Xaml.Controls名前空間)は画面の40%の幅になる(このGridコントロールが画面幅いっぱいに収まっている場合)。
ところが、場合によってはセルに入れたコントロールが「いうことを聞かない」ことがあるのだ。例えば、Hubコントロール(Windows.UI.Xaml.Controls名前空間)に先ほどのGridコントロールを入れてみよう(次のコードと画像)。
<Hub VerticalAlignment="Top">
<Hub.Header>
<TextBlock Text="{StaticResource AppName}" ……省略…… />
</Hub.Header>
<HubSection Header="SECTION 01" x:Name="Section01">
<HubSection.ContentTemplate>
<DataTemplate>
<Grid Background="Gray" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.6*" />
<ColumnDefinition Width="0.4*" />
</Grid.ColumnDefinitions>
<Image Source="Assets/hayashi_12.JPG" Stretch="Uniform" />
<TextBlock Grid.Column="1" TextWrapping="Wrap">
……省略(長い文字列)……
</TextBlock>
</Grid>
</DataTemplate>
</HubSection.ContentTemplate>
</HubSection>
……省略(二つ目のHubSection)……
</Hub>
先ほどのGridコントロールを、Hubコントロールの中のHubSectionコントロール(Windows.UI.Xaml.Controls名前空間)に置いた。
なお、別途公開のサンプルでは、このコードは共有プロジェクト(VBではPCL)に置いたユーザーコントロールの中にあり、WindowsとPhoneのそれぞれの画面に貼り付けて利用している。
Gridコントロールの比例配分がうまく行かない例(VS 2013、上:Windows/下:Phone)
Windows(上)では、右側のTextBlockコントロールが、Gridコントロールの分割を無視して横に広がってしまった(赤矢印の範囲)。実はHubSectionコントロールの幅はウィンドウの幅では決まらない。Windowsでは、HubSectionコントロールの幅はその中のコンテンツ次第なのだ。そのために、Gridコントロール自身の幅も、その列幅の配分も、想定したようにはならない。
Phone(下)では、HubSectionコントロールの幅を指定しなかったときの既定値は360ピクセルになっているため、Gridコントロール内の分割は想定通りになる。しかし固定幅では、「コントロールの幅をウィンドウ幅の一定割合にしたい」という意図は満たせない。
この例では、HubSectionコントロールの幅をウィンドウのサイズに比例して設定できればよさそうである。それにはどうすればよいのだろうか?
コードビハインドで調整する
ウィンドウのサイズが変わるときに、コントロールの幅を調整すればよいのである。Pageコントロール(Windows.UI.Xaml.Controls名前空間)でもユーザーコントロールでも、サイズが変わるときにSizeChangedイベントが発生するので、そこでウィンドウの幅を取得して、その値を基に算出した幅を目的のコントロールに設定すればよい(次のコードと画像)。
<UserControl
x:Class="MetroTips105CS.MyUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
……省略……
SizeChanged="UserControl_SizeChanged"
>
Pageコントロールやユーザーコントロールの冒頭で、SizeChangedイベントハンドラーの設定を追加する(太字の部分)。ここではユーザーコントロールを使っているが、Pageコントロールでも追加する部分は同じである。
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
{
double newWidth = Window.Current.Bounds.Width * 0.9; // ウィンドウの幅を取得し、その90%を求める
this.Section01.Width = newWidth; // 一つ目のHubSectionコントロールの幅を設定
this.Section02.Width = newWidth; // 二つ目のHubSectionコントロールの幅を設定
}
Private Sub UserControl_SizeChanged(sender As Object, e As SizeChangedEventArgs)
Dim newWidth As Double = Window.Current.Bounds.Width * 0.9 ' ウィンドウの幅を取得し、その90%を求める
Me.Section01.Width = newWidth ' 一つ目のHubSectionコントロールの幅を設定
Me.Section02.Width = newWidth ' 二つ目のHubSectionコントロールの幅を設定
End Sub
ここでは、幅を調整したいコントロールが二つある(二つのHubSectionコントロール)。それぞれの幅をウィンドウの90%に設定している。
なお、ユーザーコントロールでは、ウィンドウの幅よりも自身の幅(自身のActualWidthプロパティ)を使う方が適切かもしれない。
コードビハインドでコントロールの幅を調整した結果
HubSectionコントロールの幅(=Gridコントロールの幅)は、画面の幅に応じて変化している。
Gridコントロールの横の分割(左に画像/右に文章)は、常に6対4となっている。
カスタムビヘイビアーで調整する
調整する対象のコントロールが増えてくると、コードビハインドでサイズを調整するのが面倒になってくる。そんなときは、カスタムビヘイビアーを作成して使うとよい。
そのようなカスタムビヘイビアーのサンプルは、探せばいくつも見つかるだろう。ここでは「Tips and tricks for using XAML controls in your universal Windows apps」というブログ記事の「Window-based width adaption」という項に掲載されている「WindowDimensionBehavior」クラスというカスタムビヘイビアーを使わせてもらうことにする。そのC#のコードは長いのでここでは省略させていただくが、別途公開のサンプルには収録してある。なお、カスタムビヘイビアーはPCLでは使えないため、別途公開サンプルのVBのコードにはこの方法を含めていない(Windows/Phone個別のプロジェクトならば利用可)。
カスタムビヘイビアーを使うには、あらかじめプロジェクトに「Behaviors SDK (XAML)」への参照を追加しておく(次の画像)。
Behaviors SDKへの参照をプロジェクトに追加する(VS 2013)
カスタムビヘイビアーを使うために、プロジェクトにBehaviors SDKへの参照を追加しておく(PCLでは利用できない)。
参照を追加するには、追加したいプロジェクトをソリューションエクスプローラーで選んで右クリック、コンテキストメニューで[追加]−[参照]を選ぶ。するとこの画像のような[参照マネージャー]ダイアログが出てくるので、左側で[Windows 8.1](または[Windows Phone 8.1])−[拡張]と選び、中央で[Behaviors SDK (XAML)]にチェックを入れ、[OK]ボタンをクリックする。この作業は、Windows/Phoneそれぞれのプロジェクトで行う。
WindowDimensionBehaviorカスタムビヘイビアーを使ってコントロールの幅を調整するXAMLコードは、次のようになる。
<UserControl
x:Class="MetroTips105CS.MyUserControl2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
……省略……
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
>
……省略……
<HubSection Header="SECTION 01">
<!-- HubSectionコントロールの幅を、画面幅の90%にする -->
<Interactivity:Interaction.Behaviors>
<local:WindowDimensionBehavior WidthPercentage="0.9"/>
</Interactivity:Interaction.Behaviors>
<HubSection.ContentTemplate>
<DataTemplate>
<Grid Background="Gray" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.6*" />
<ColumnDefinition Width="0.4*" />
</Grid.ColumnDefinitions>
……省略……
「Gridコントロールの比例配分がうまく行かない例(XAML)」に掲載したXAMLコードに、太字の部分を追加した。WindowDimensionBehaviorクラスを使った幅の指定は、コントロールごとに行う。
なお、WindowDimensionBehaviorクラスの名前空間は、このユーザーコントロールと同じにしてある(異なる場合は名前空間定義の追加と、接頭辞「local:」の修正が必要)。
実行結果は、前述のコードビハインドで調整したときと同様になる。
まとめ
ウィンドウ幅を基準にしてコントロールの幅を調整するには、コードビハインドで調整するか、カスタムビヘイビアーを作成して使用するとよい。調整対象のコントロールが多数あるときは、XAMLコードの書き換えだけで済むカスタムビヘイビアーが楽だ。
本稿では幅だけを扱ったが、高さでも同様である。Windows 8.1/Windows Phone 8.1用に作ったユニバーサルWindowsアプリをWindows 10で動かすと、高さも思いの他小さくなり得る(画面によるが、1024×768ピクセルのモニターではウィンドウの高さが500ピクセルほどになるようだ)。ウィンドウサイズに応じたUIが求められたときには、このTIPSも思い出してほしい。
Copyright© Digital Advantage Corp. All Rights Reserved.