リキッド レイアウト (liquid layout) とは、表示領域のサイズによって動的に表示方法が最適化されるようなレイアウトを指します。
もしレイアウトについてとくに意識せずに固定サイズのアプリケーションを実装してしまうと、
ウィンドウのサイズが小さい場合には一部のコンテンツが隠れたり、
逆にウィンドウのサイズが大きい場合には空白ができたりしてしまいます。
ディスプレイの解像度の制限やユーザーによるウィンドウのサイズ変更など、
表示領域のサイズの連続的な変化に対応しなければなりません。
以下では、XAML 系アプリケーションでリキッド レイアウトを実現するためのパターンと実装例を紹介します。
(1) Grid
Grid では、ColumnDefinitions プロパティおよび RowDefinitions プロパティにより、各行・列の幅を指定できます。
固定値のほか、他の行・列との相対値 (1:2 にする場合など) や内部のコンテンツの幅 (Auto) も指定できます。
(2) ScrollViewer
ScrollViewer を利用すれば、スクロールにより全体を表示させることができます。
コレクション データを扱う場合は、ScrollViewer と WrapPanel を組み合わせて使うとよいでしょう。
ちなみに ScrollViewer は、既定で縦スクロールバーが有効、横スクロールバーが無効に設定されています。
(3) Viewbox
Viewbox は、表示領域に合わせて内部のコンテンツを伸縮させることにより、元のレイアウトを保持します。
最も手っ取り早い方法かもしれません。
(4) 最大値・最小値の指定
Viewbox のような伸縮させるコントロールでは、既定では無制限にコンテンツが拡大・縮小してしまいます。
したがって、サイズの最大値・最小値を指定する方法も有効です。
これらのパターンを実装する例として、次の WPF アプリケーションを作成しました。
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
namespace LayoutWpf | |
{ | |
public class AppModel | |
{ | |
public int[] Numbers { get; private set; } | |
public AppModel() | |
{ | |
Numbers = Enumerable.Range(1, 15).ToArray(); | |
} | |
} | |
} |
<Window | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
xmlns:local="clr-namespace:LayoutWpf" x:Class="LayoutWpf.MainWindow" | |
Title="MainWindow" Width="900" Height="600" FontSize="32"> | |
<Window.Resources> | |
<ItemsPanelTemplate x:Key="NumbersPanelTemplate"> | |
<WrapPanel IsItemsHost="True"/> | |
</ItemsPanelTemplate> | |
<DataTemplate x:Key="NumberDataTemplate"> | |
<TextBlock Text="{Binding Mode=OneWay, StringFormat=No.\{0\}}" Width="120" TextAlignment="Center"/> | |
</DataTemplate> | |
</Window.Resources> | |
<Window.DataContext> | |
<local:AppModel/> | |
</Window.DataContext> | |
<Grid> | |
<Grid.ColumnDefinitions> | |
<ColumnDefinition/> | |
<ColumnDefinition/> | |
</Grid.ColumnDefinitions> | |
<Grid.RowDefinitions> | |
<RowDefinition/> | |
<RowDefinition/> | |
</Grid.RowDefinitions> | |
<Grid Margin="20" Background="#FFEECCBB"> | |
<ItemsControl ItemsSource="{Binding Numbers}" ItemsPanel="{DynamicResource NumbersPanelTemplate}" ItemTemplate="{DynamicResource NumberDataTemplate}"/> | |
</Grid> | |
<ScrollViewer Grid.Column="1" Margin="20" Background="#FFCCEEBB"> | |
<ItemsControl ItemsSource="{Binding Numbers}" ItemsPanel="{DynamicResource NumbersPanelTemplate}" ItemTemplate="{DynamicResource NumberDataTemplate}"/> | |
</ScrollViewer> | |
<Viewbox Grid.Row="1" Margin="20"> | |
<Grid Width="390" Height="260" Background="#FFBBCCEE"> | |
<ItemsControl ItemsSource="{Binding Numbers}" ItemsPanel="{DynamicResource NumbersPanelTemplate}" ItemTemplate="{DynamicResource NumberDataTemplate}"/> | |
</Grid> | |
</Viewbox> | |
<Grid Grid.Row="1" Grid.Column="1" Margin="20" Background="#FFFFEEBB"> | |
<Viewbox MaxWidth="360" HorizontalAlignment="Left" VerticalAlignment="Top"> | |
<Grid Width="360" Background="LightGray"> | |
<ItemsControl ItemsSource="{Binding Numbers}" ItemsPanel="{DynamicResource NumbersPanelTemplate}" ItemTemplate="{DynamicResource NumberDataTemplate}"/> | |
</Grid> | |
</Viewbox> | |
</Grid> | |
</Grid> | |
</Window> |
(全体のソースコードは GitHub の LayoutWpf にあります。)
ここでは 4 つの表示領域があり、
左上はとくに何も対策しない例、右上は ScrollViewer を使う例、
左下は Viewbox を使う例、右下は Viewbox の横幅に最大値を指定する例です。
各領域では、内部のコンテンツとしてコレクション データを ItemsControl で表示させています。
この ItemsControl の ItemsPanel を WrapPanel に設定しています。
また、この 4 つの表示領域は 2×2 の Grid 上にあり、行も列も 1:1 に分割されています。
このアプリケーションを起動するとこうなります。
ウィンドウのサイズを小さくします。
左上および右上では WrapPanel の列の数が減りますが、
左上では、下部を表示することができません。右上では、スクロールすれば残りが表示されます。
左下と右下では Viewbox に合わせてコンテンツが縮小されます。
今度は、ウィンドウのサイズを大きくします。
左上および右上では WrapPanel の列の数が増えています。
左下では Viewbox に合わせてコンテンツが拡大されるのに対し、右下では Viewbox が一定以上は大きくなりません。
作成したサンプル
LayoutWpf (GitHub)