WPF 標準コントロールデモアプリ › Grid

Grid Layout

Grid は子要素を行と列に並べるパネルです。行の高さと列の幅は、固定値、内容に合わせる Auto、残りのスペースを比率で分ける * のいずれかで決めます。

3 種類のサイズ指定が幅を分け合うしくみ

Grid は行を RowDefinition、列を ColumnDefinition で定義し、子要素は添付プロパティ Grid.Row と Grid.Column(0 始まり)でセルを選びます。3 種類のサイズ指定は組み合わせて使えます。幅 440 の Grid に Auto | 80 | 1* | 2* の 4 列を定義し、各列に幅 60 の内容を置くと、列幅は 60 / 80 / 100 / 200 になりました。* の 2 列は、残りの 300 を 1:2 で分けています。同じ Grid を幅 740 に広げると、変わったのは * の 2 列だけで、200 / 400 になりました。

MinWidth と MaxWidth は、固定値の列だけでなく Auto 列にも効きます。内容の幅が 60 の Auto 列に MaxWidth="30" を設定すると、列幅は 30 になりました。値が矛盾するときは MinWidth が優先されます。デモアプリの初期値 Width="25" MinWidth="30" MaxWidth="100" では列幅は 30 になり、MinWidth="100" MaxWidth="50" では 100 になりました。行の高さも同じ規則で決まります。デモアプリの初期値(Height="20" MinHeight="10" MaxHeight="100")では高さ 20、Height="5" MinHeight="10" では 10、内容の高さが 40 の Auto 行に MaxHeight="15" を設定すると 15 になりました。

Auto と * が交差すると、子要素が測り直されることがあります。列が Auto, *、行が *, Auto の 2 × 2 の Grid では、Auto 列と * 行が交わるセルの子要素が初回レイアウトで 2 回測られました。固定値だけ、Auto だけ、* だけの場合は、どの子要素も 1 回でした。測るのに時間のかかる子要素を置くときに影響します。

子要素がどのセルに置かれるか

Grid.Row と Grid.Column の既定値はどちらも 0 なので、指定しなかった子要素はすべて最初のセルに置かれて重なります。範囲外の値を指定してもエラーにはなりません。

例外になるのは不正な数値だけで、負の番号と 0 の Span は ArgumentException になります。それ以外は警告なしに丸められます。Grid.Column は ColumnDefinition への参照ではなく番号なので、定義を削除しても子要素の番号はそのままです。削除後にその番号を持つ列に置かれ、番号が新しい最後の列を超えていれば、上で見た丸めのとおり警告なしに最後の列へ置かれます。定義を削除したときは子要素を確認します(削除そのものは測っていません)。

条件 計測値
Grid.Row / Grid.Column の既定値(メタデータ) 0 / 0
2x2、Grid.Row/Column を指定しない子が 2 つ x=0 y=0 w=150 h=100 | x=0 y=0 w=150 h=100
2x2、Grid.Column=2(範囲外) x=150 y=0 w=150 h=100
2x2、Grid.Column=5、Grid.Row=5 x=150 y=100 w=150 h=100
3x2、Grid.ColumnSpan=5 x=0 y=0 w=300 h=100
3x2、Grid.Column=1、ColumnSpan=5 x=100 y=0 w=200 h=100
3x2、Grid.RowSpan=5 x=0 y=0 w=100 h=200
Grid.SetColumn(child, -1) ArgumentException
Grid.SetColumnSpan(child, 0) ArgumentException
定義なし、子 A と子 B(Column=1、50x30) x=0 y=0 w=300 h=200 | x=125 y=85 w=50 h=30

300 × 200 の Grid で子要素が置かれた位置(x, y, 幅, 高さ)。.NET 10 / Windows 11 で計測。

* 列が比率を保つ条件

* 列が比率を保つのは、Grid に有限の幅が与えられたときだけです。StackPanel や ScrollViewer の中でも * 列が 0 に潰れることはありませんが、比率が崩れることがあります。

行も縦方向に同じように振る舞います。ScrollViewer の中で Auto 行を使うのは問題ありません。高さ 120 の ScrollViewer に、高さ 100 の Auto 行を 3 つ持つ Grid を入れると、Grid の高さは 300 になり、縦のスクロールバーが表示されました。

入れ物 大きさ スクロールバー
横向きの StackPanel、幅 400 列 60 / 30 -
縦向きの StackPanel、幅 400 列 133.33 / 266.67 -
幅 400 の ScrollViewer、横 Disabled 列 133.33 / 266.67 横 Collapsed
幅 400 の ScrollViewer、横 Auto 列 133.33 / 266.67 横 Collapsed
幅 60 の ScrollViewer、横 Disabled 列 20 / 40 横 Collapsed
幅 60 の ScrollViewer、横 Auto 列 60 / 30 横 Visible
高さ 300 の ScrollViewer、行 1* | 1*(内容 20) 行 150 / 150、スクロールできる量 0 -
高さ 120 の ScrollViewer、縦 Auto、行 Auto x3(それぞれ 100) Grid 300、スクロールできる量 180 縦 Visible

親要素ごとの * 列・* 行の大きさ。どの場合も 0 には潰れない。.NET 10 / Windows 11 で計測。

別々の Grid の列をそろえる

SharedSizeGroup を使うと、同じグループ名を付けた列(または行)の幅を、別々の Grid の間でそろえられます。フォームの各行を個別の Grid で組むときや、ItemsControl の各項目がそれぞれ Grid を持つときに、ラベル列の幅をそろえる方法です。共通の親要素に Grid.IsSharedSizeScope="True" を設定します。内容の幅が 50 と 120 の場合、2 つの Auto 列はスコープなしで 50 / 120、スコープありで 120 / 120 になりました。

共有グループに入れた * 列は Auto と同じ扱いになり、残りのスペースを埋めずに 120 / 120 になりました。デモアプリでは、チェックボックスでスコープを切り替え、テキストボックスで各 Grid の内容を変えられます。

重なり、ZIndex、空いている部分

1 つのセルに複数の子要素を置くと、重なって表示されます。Panel.ZIndex を指定しない場合は後から追加した子要素が手前になり、先の要素に ZIndex="1" を設定すると手前に来ました。比べられるのは同じパネルの子要素どうしだけです。入れ子の Grid の中の要素に ZIndex="100" を設定しても、入れ子の Grid の後にある兄弟要素より手前には出ず、入れ子の Grid 自体の ZIndex を上げると手前に出ました。読み込み中の表示を内容と同じセルに重ねるオーバーレイは、このしくみで作ります。

Background は、Grid の空いている部分がマウスに反応するかどうかを決めます。既定値の null では、Grid の空いている部分をヒットテストすると、背後の要素に当たりました。Transparent を設定すると、同じ位置で Grid に当たりました。色を塗らずに領域全体でクリックや MouseEnter を受けたい場合は Transparent を設定します。

グリッド線と入れ子

ShowGridLines は行と列の境界に線を引き、セルの大きさを確かめやすくします。線は内部の専用のビジュアル(GridLinesRenderer)が描き、Grid の公開プロパティのうち線に関係するのは ShowGridLines だけです。線の色・太さ・種類は変えられないので、デザインとして見せる罫線には Border を使います。

Grid の入れ子自体のコストは小さいものでした。500 個の TextBlock を並べる場合、行ごとに Grid を入れ子にしても、初回レイアウトの時間は平坦な Grid と比べて、繰り返し測って 1 割程度の差に収まりました。すべての TextBlock を 3 重の Grid で包み、Grid を 1,500 個増やすと、1〜2.5 割ほど長くなりました。列を * から Auto に変えても差は出ませんでした。構造は読みやすさで選んで構いません。

デモアプリで試す

デモアプリの Grid のページ。左にコントロールの一覧、右に最初の節の Column / Row

デモアプリの Grid のページには、上で扱った各プロパティに専用の欄があり、入力欄で値を変えると結果がその場で変わります。欄ごとの「Show Code」リンクで、その欄の XAML も表示できます。次の XAML は ColumnSpan / RowSpan の欄(GridUsageControl.xaml)から、スタイルと周囲の GroupBox を省き、名前空間の宣言を加えたものです。コンボボックスで選んだ値が、1 つ目と 2 つ目の TextBlock の Span にバインドされます。

<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <ComboBox x:Name="ColumnSpanComboBox" SelectedIndex="0" SelectedValuePath="Content">
    <ComboBoxItem Content="1" />
    <ComboBoxItem Content="2" />
  </ComboBox>
  <ComboBox x:Name="RowSpanComboBox" SelectedIndex="0" SelectedValuePath="Content">
    <ComboBoxItem Content="1" />
    <ComboBoxItem Content="2" />
  </ComboBox>

  <Grid x:Name="ColumnSpanRowSpanResultGrid">
    <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition />
      <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition />
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0" Grid.Column="0"
               Grid.ColumnSpan="{Binding SelectedValue, ElementName=ColumnSpanComboBox}"
               Background="CadetBlue" Text="1x1,ColumnSpanSetting" />
    <TextBlock Grid.Row="0" Grid.Column="2"
               Grid.RowSpan="{Binding SelectedValue, ElementName=RowSpanComboBox}"
               Background="SteelBlue" Text="1x3,RowSpanSetting" />
    <TextBlock Grid.Row="1" Grid.Column="0" Background="CornflowerBlue" Text="2x1" />
    <TextBlock Grid.Row="1" Grid.Column="1" Background="LightBlue" Text="2x2" />
  </Grid>
</StackPanel>

関連するコントロールと記事

ソースコードと計測の方法

このページの挙動の記述は、すべて .NET 10 / Windows 11 で実際に動かして確かめたものです。計測には、このサイトのスクリーンショット生成ツールの GridDemoScene を使いました。

GitHub で Grid のソースコードを見る →