imani-cの日記

WPFスタイルとテンプレートの本をAmazonで出しました。ちょっととっつきにくいこれらについて知りたいお方は、覗いてみてください。「WPF スタイル」で検索すると、トップに出ます。gRPCの本もあります。「gRPC入門」で検索するとすぐ見つかります。

WPF - GridのStarによる高さと幅の指定

Gridは、コントロールを縦横に配置するのに都合の良いコンテナです。Grid内部を行と列に分けて、それぞれにコントロール配置することにより、複数のコントロールをよく整理された印象を与えるように配置できます。

目次

Gridの行と列の宣言

Gridの行と列の定義は、以下のコード例のように行われます。

<Grid>
    <!-- 行を定義する -->
    <Grid.RowDefinitions>
        <RowDefinition Width="20"/>         <!-- ピクセル数で幅を指定する。 -->
        <RowDefinition Width="Auto"/>       <!-- 表示内容により自動的に幅を計算する。 -->
        <RowDefinition Width="*"/>          <!-- 加重配分により幅を計算する。 -->
    </Grid.RowDefinitions>

    <!-- 列を定義する -->
    <Grid.ColumnDefinitions>
        <ColumnDefinition Height="20"/>     <!-- ピクセル数で高さを指定する。 -->
        <ColumnDefinition Height="Auto"/>   <!-- 表示内容により自動的に高さを計算する。 -->
        <ColumnDefinition Height="*"/>      <!-- 加重配分により高さを計算する。 -->
    </Grid.ColumnDefinitions>
</Grid>

行も列も同じなので、以下では行についてだけ説明します。

ピクセル数で高さを指定した場合

行の高さは、指定されたピクセル数になります。内容がそのサイズより大きくても小さくても、配慮されません。

Auto:自動計算

行の高さは、行の内容が占める高さになります。

その高さにより行がGrid本体をはみ出す場合には、はみ出した分は表示されませんし、下にある行もはみ出して表示されなくなります。

*:加重配分

行の高さは、Gridの利用できる高さを加重配分したものになります。

例えば、上記のコードで、Gridの高さを100、2行目のAutoで決まる行の高さを30とすると、3行目のStar (つまり*)には、「100(Gridの高さ) - 20(1行目の高さ) - 30(2行目の高さ)」が与えられて50となります。公式の説明がマイクロソフトサイトの「パネルの概要」にあります。

自動配置におけるStar

WPFを利用する大きなメリットは自動配置です。上記の例であれば、Gridのサイズ自体をその中身によって決めることができます。画面に表示する内容が増減したり文言が変わったりすることは、ソフト開発ではよくあることです。そのようなとき、Gridのサイズや、それを収めるWindowのサイズを自動的に計算させるようにしておけば、サイズ調整という面倒で厄介な作業をせずに済みます。

自動配置を使うとき、当然ながらGridの高さを「Auto」にして、表示内容から自動的に計算させます。上記の説明では、行の高さをStarにするとGridの高さから行の高さ(つまり表示内容の高さ)を計算することになり、循環参照が生じて何が起きるかわからなくなります。

実際には循環参照など起こさず、Gridの高さの代わりに、Gridがとりうる最大の高さを用いて加重配分が行われます。

行の高さの指定方法まとめ

行の高さの指定方法を一覧にすると、以下のようになります。

指定方法 Gridの高さが指定されている Gridの高さの上限が指定されている Gridの高さに上限がない
数値 指定された高さ 同左 同左
Auto 内容物が入る高さ。Gridの高さに収まるか否かは考慮されない 同左 同左
Star (*) 加重配分 同左
ただし、Grid高さの上限から高さが決まっている行の高さを引き、余りを加重配分した高さが各行高の上限になる。
同左
ただし、Grid高さの上限として、Gridを含むコントロールの高さ(Autoの場合には高さの上限)が用いられる。

自動配置とStarの利用例

自動配置とStarの応用例を掲げます。 犬の名前や犬種をポップアップさせたウィンドウに表示します。

<Window x:Class="PracticalGridSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow"
        FontSize="20"
        d:DesignHeight="450" d:DesignWidth="800"
        WindowStyle="ToolWindow"
        SizeToContent="WidthAndHeight">

    <Grid Height="Auto" MaxHeight="500">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- 1行目の表示 -->
        <TextBlock Text="Dog info" Background="Silver" Grid.Row="0"/>

        <!-- 2行目の表示。内容によって高さが変わる -->
        <StackPanel Grid.Row="1">
            <TextBlock Text="{Binding DogInfo.Name, StringFormat=Name: {0}}"/>
            <TextBlock Text="{Binding DogInfo.Breed, StringFormat=Dog breed: {0}}"/>
        </StackPanel>
    </Grid>
</Window>
using System.Windows;

namespace PracticalGridSample
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            DataContext = this;
            InitializeComponent();
        }

        public DogInfoClass DogInfo { get; } = new DogInfoClass { Name="Taro", Breed="Akita", Weight=30.0};
    }

    public class DogInfoClass
    {
        public string Name { get; set; }
        public string Breed { get; set; }
        public double Weight { get; set; }
    }
}

サンプルの表示

この例では、Gridの高さを表示内容に合わせ、Windowの高さをGridの高さに合わせています。そのため、ウィンドウ全体に犬の名前と犬種が表示されています。

ここで体重も表示するように仕様変更があった場合には、単純に体重用の行を追加するだけで、ウィンドウの高さまで自動的に変わります。

        <!-- 2行目の表示。内容によって高さが変わる -->
        <StackPanel Grid.Row="1">
            <TextBlock Text="{Binding DogInfo.Name, StringFormat=Name: {0}}"/>
            <TextBlock Text="{Binding DogInfo.Breed, StringFormat=Dog breed: {0}}"/>
            <TextBlock Text="{Binding DogInfo.Weight, StringFormat=Dog breed: {0}}"/>   <!-- この行を追加 -->
        </StackPanel>

体重表示を追加

GridのMaxHeightを500にしていますが、この数値に根拠があるわけではなく、ウィンドウがあまり大きいとポップアップに見えないというだけの理由です。Gridの高さに上限があるので、表示するデータが増えすぎると、Gridの領域からはみ出してしまいます。そのような場合には、StackPanelをScrollViewerの中に置くなどの工夫が必要になります。でも、そうなるまでは、仕様変更による余分なコード修正が生じずにすみます。